openGauss-向量化执行引擎-索引扫描CStoreIndexScan
openGauss实现了向量化执行引擎,达到算子级别的并行。也就是说在执行器火山模型基础上,一次处理一批数据,而不是一次一个元组。这样可以充分利用SIMD指令进行优化,达到指令级别并行。本文关注索引扫描算子CStoreIndexScan,并以btree索引为例。
1、Btree索引
openGauss基于PostgreSQL,btree的索引页分为meta页、root页、branch页和leaf页。其中meta页结构和其他页结构如下:
整体一个Btree索引如下所示:
PG 中的 BTree 是一个略有变动的 B-Link-Tree,即内部节点增加了左向指针。基本的BTree 索引持久化形态是和 Heap 表基本一样(除了唯一的Meta page),每一个节点保存到一个page,节点内的key 已经 子节点保存的value都会作为index tuple 保存到该 page中。
1)Meta page 用来保存当前BTree 的元数据,也是索引page的第0页
2)Inner Node(page) 内部节点,其index-tuple 中 仅保存key 以及指向子节点的指针
3)Leaf Node(page) 叶子节点,是BTree 索引的最底层,其内部的 index-tuple 保存的是key 以及 指向 数据tuple的 tid。通过 tid 能够访问这个key 对应的实际数据。
每一个节点所在的page 内部最后有一部分 Special space空间,保存了当前节点或者说 index-page 的元数据,其是通过 BTPageOpaqueData表示。主要保存这个 page 左右方向指针、page所在 BTree 层级、以及当前页面的状态(叶子节点/内部节点/根节点/删除页面......)
4)通过索引查询时,先通过meta页定位到root页,然后通过key值在页面进行二分查找。依次递进,直到在索引leaf 页找到对应索引条目。该索引条目包含heap页的页号和heap记录的offsetnumber。通过该信息即可定位到具体的heap条目。
2、向量化索引扫描算子
openGauss通过CStoreIndexScan算子进行向量化索引扫描。其实具体到索引上,比如btree索引,仍旧是沿用原有逻辑进行扫描,只不过将ItemPointerData存入VctorBatch中,然后将其再存入Batchsortstate进行排序,最后从排序结果中拿取VectorBatch。以此保证取出的ItemPointerData都是根据页号排序的,避免了heap页的随机读取。向量化索引扫描的优势:兼容向量化引擎其他算子,以达到全算子向量化,减少VecToRow和RowToVec的互相转换;同时减少底层算子函数的调用;因为增加了排序,可如同bitmap扫描一样减少heap页的随机访问。