一. 前言
在openGauss中,BitmapOr算子扫描是指谓词条件是索引列的or条件时,将多个or条件的索引谓词先组成一个需要扫描元组的BitmapOr扫描算子,然后再通过BitmapOr算子的tid信息去扫描元组,从而减少or条件带来的随机扫描过多的问题,如下所示:
本文主要通过走读代码了解openGauss是怎么实现BitmapOr算子的功能的。
二. 执行计划生成
执行计划生成主要是如何在堆扫描下生成一个BitmapOr算子,BitmapOr算子包含所要扫描的元组信息。执行计划生成的入口在create_index_paths函数中,主要的代码流程如下所示:
create_index_paths
indexpaths = generate_bitmap_or_paths(root, rel, rel->baserestrictinfo, NIL, false);
foreach (lc, clauses) {
if (!restriction_is_or_clause(rinfo)) // 只支持or语句
continue;
foreach (j, ((BoolExpr*)rinfo->orclause)->args) { // 遍历各个or条件
build_paths_for_OR
match_clauses_to_index(index, clauses, &clauseset); // 检查索引和谓词的匹配情况
indexpaths = build_index_paths // 每个or条件只是建立一个普通索引
result = list_concat(result, indexpaths); // 把当前的索引建出来的路径保存到结果中
bitmapqual = choose_bitmap_and(root, rel, indlist, globalIndexList); // 合并多索引情况下的bitmap和挑选代价最小的索引
for (i = 0; i < npaths; i++) {
if (i == 0 || chooseInfo.costsofar < bestcost) {
bestpaths = chooseInfo.paths;
bestcost = chooseInfo.costsofar;
}
}
pathlist = lappend(pathlist, bitmapqual);
}
bitmapqual = (Path*)create_bitmap_or_path(root, rel, pathlist); // 所有约束参数的索引连起来形成bitmap索引
cost_bitmap_or_node(pathnode, root);
foreach (l, path->bitmapquals) {
cost_bitmap_tree_node(subpath, &subCost, &subselec);
selec += subselec; // bitmap or的代价为各个or索引的代价之和
}
}
indexpaths = generate_bitmap_or_paths(root, rel, joinorclauses, rel->baserestrictinfo, false); // 如果有or条件的话,也利用or条件作为约束生成一个新的bitmap path
bitmapqual = choose_bitmap_and(root, rel, bitindexpaths); // 如果有多个bitmap索引路径生成,挑选出代价最小的
bpath = create_bitmap_heap_path(bitmapqual); //生成最后的堆扫描路径,bitmapqual条件为上述生成的bitmapor路径
add_path(root, rel, (Path*)bpath); // 保存路径
三. 算子层的实现
算子层的实现入口在BitmapHeapTblNext函数中,代码流程如下所示:
BitmapHeapTblNext
if (tbm == NULL) { 初始化需要扫描元组tid的bitmap
MultiExecProcNode(outerPlanState(node)); // 获取需要访问的元组的tid,并且生成bitmap
MultiExecBitmapIndexScan
scan_handler_idx_getbitmap(scandesc, tbm)
index_getbitmap(scan, bitmap)
btgetbitmap_internal(IndexScanDesc scan, TIDBitmap *tbm)
for (;;) { // 根据谓词条件确认访问元组的上下界,并且将对应的位置加入到bitmap中
tbm_handler._add_tuples(tbm, heapTid, 1, false, currPartOid, bucketid); // 把索引的元组位置转成bitmap
}
TableScanBitmapNextBlock(scan, tbmres, &node->ss.ps.state->have_current_xact_date)
heapam_scan_bitmap_next_block
hscan->rs_base.rs_cbuf = ReleaseAndReadBuffer(); // 拿到buff
for (curslot = 0; curslot < tbmres->ntuples; curslot++) {
OffsetNumber offnum = tbmres->offsets[curslot];
hscan->rs_base.rs_vistuples[ntup++] = ItemPointerGetOffsetNumber // 获取位置信息
}
}
TableScanBitmapNextTuple
HeapamScanBitmapNextTuple
targoffset = hscan->rs_base.rs_vistuples[hscan->rs_base.rs_cindex];
lp = PageGetItemId(dp, targoffset); // 获取到元组所在的位置
hscan->rs_ctup.t_data = (HeapTupleHeader)PageGetItem((Page)dp, lp); // 拿到数据
hscan->rs_base.rs_cindex++; // ++ 指向下一次读取的位置