一. 前言
openGuass支持在做nestloop的时候,支持通过Materialize的方式将内表缓存到内存中,然后外表的数据内表数据进行碰撞的时候,如果内表已经缓存了数据,那么直接从缓存中直接读取内表的数据,从而实现内部数据Reuse。如下所示,不Reuse内表时内部的数据需要读三遍才能完成Join操作,但是内部数据Reuse时,只需要读取一遍即可。
在处理内表的“abc”时,外表的”abc“,”ecf“,”klm"的数据要到物理表中读取,但是处理内表的“ecf”和“klm”的数据时,外表不需要再到物理表中读取数据,只需要从物化到内存中读取缓存的数据即可。
本文主要走读代码了解openGauss中如何实现外表的物化Reuse能力。
二. 执行计划生成
openGuass在NestLoop Join,Append等场景下都会生成物化算子,如下为nestloop场景下生成物化算子的代码执行流程:
add_paths_to_joinrel
match_unsorted_outer(RelOptInfo* outerrel, RelOptInfo* innerrel)
foreach (lc1, outerrel->cheapest_total_path) {
foreach (lc2, innerrel->cheapest_total_path) {
if (u_sess->attr.attr_sql.enable_material) {
matpath = (Path*)create_material_path(inner_cheapest_total); // 尝试将内表物化
cost_material
run_cost += 2 * u_sess->attr.attr_sql.cpu_operator_cost * tuples // 物化算子的代价
}
}
}
三. 物化算子的实现
物化的算子实现入口在ExecMaterial函数中,其中分成两种实现,一种是并行需要的ExecMaterialAll实现,另外一种就是普通的ExecMaterialOne实现。ExecMaterialOne实现基本上和https://blog.csdn.net/wangfeihuo/article/details/141786234?spm=1001.2014.3001.5501 所说的流程一样。ExecMaterialAll会先把内表的数据全部完成缓存后再返回,主要流程如下所示:
ExecMaterialAll
for (;;) { // for 循环会循环取内表的数据,直到取完后退出
PlanState* outerNode = outerPlanState(node);
TupleTableSlot* outerslot = ExecProcNode(outerNode); // 直接从物理表读取数据
/* subPlan may return NULL */
if (TupIsNull(outerslot)) { // 内表的数据已经取完
break;
}
tuplestore_puttupleslot(tuple_store_state, outerslot); // 缓存
}
tuplestore_gettupleslot(tuple_store_state, forward, false, slot) // 从缓存中读取数据
return slot; // 返回元组数据