一. 前言
在openGauss中,BitmapOr算子扫描是指谓词条件是索引列的or条件时,将多个or条件的索引谓词先组成一个需要扫描元组的BitmapOr扫描算子,然后再通过BitmapOr算子的tid信息去扫描元组,从而减少or条件带来的随机扫描过多的问题,如下所示:
本文主要通过走读代码了解openGauss是怎么实现BitmapOr算子的功能的。
二. 执行计划生成
执行计划生成主要是如何在堆扫描下生成一个BitmapOr算子,BitmapOr算子包含所要扫描的元组信息。执行计划生成的入口在create_index_paths函数中,主要的代码流程如下所示:
create_index_pathsindexpaths = 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_ORmatch_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 pathbitmapqual = choose_bitmap_and(root, rel, bitindexpaths); // 如果有多个bitmap索引路径生成,挑选出代价最小的bpath = create_bitmap_heap_path(bitmapqual); //生成最后的堆扫描路径,bitmapqual条件为上述生成的bitmapor路径add_path(root, rel, (Path*)bpath); // 保存路径
三. 算子层的实现
算子层的实现入口在BitmapHeapTblNext函数中,代码流程如下所示:
BitmapHeapTblNextif (tbm == NULL) { 初始化需要扫描元组tid的bitmapMultiExecProcNode(outerPlanState(node)); // 获取需要访问的元组的tid,并且生成bitmapMultiExecBitmapIndexScan 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_blockhscan->rs_base.rs_cbuf = ReleaseAndReadBuffer(); // 拿到bufffor (curslot = 0; curslot < tbmres->ntuples; curslot++) {OffsetNumber offnum = tbmres->offsets[curslot];hscan->rs_base.rs_vistuples[ntup++] = ItemPointerGetOffsetNumber // 获取位置信息}}TableScanBitmapNextTupleHeapamScanBitmapNextTupletargoffset = 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++; // ++ 指向下一次读取的位置