一. 前言
在openGuass中,如果对索引列执行max/min操作,openGauss会优化成只读取索引的最前/后的一行数据,避免了对整表数据进行读取和聚合操作,如下所示:
二. min/max优化代码走读
1. 首先需要将min/max 算子转成成执行计划中降序/升序的keypath,代码流程如下所示:
preprocess_minmax_aggregates
find_minmax_aggs_walker // 从pg_aggregate中查出max/min对应的aggsortop,max对应的是>, min对应的是<
get_equality_op_for_ordering_op //根据aggsortop到pg_amop表中查找对应的op信息,如amopstrategy等,对应是查询中是需要顺序扫描还是逆序扫描
build_minmax_path
sortcl->eqop = eqop;
sortcl->sortop = sortop
parse->sortClause = st_make1(sortcl); // 已经将max/min聚合操作映射成排序操作,max对应的是逆序,min对应着是顺序
parse->limitCount = makeConst(1) // 其实已经将执行计划中的max替换成了order by desc limit 1,min被替换成了 order by asc limit 1
query_planner
construct_pathkeys
construct_pathkeys
make_pathkeys_for_sortclauses
make_pathkey_from_sortop // 根据第一步拿到的aggsortop生成pathkey
make_pathkey_from_sortinfo
strategy = reverse_sort ? BTGreaterStrategyNumber : BTLessStrategyNumber
makePathKey(strategy) 在执行计划中将min/max映射成的keypath信息保存下来
2. 生成索引路径的时候,根据keypath的信息生成顺序扫描路径还是逆序扫描路径,主要代码如下所示:
build_index_paths
if(useful_pathkeys != NIL) {
ipath = create_index_path(index_is_ordered ? ForwardScanDirection : NoMovementScanDirection) // 首先生成正向的扫描路径
}
index_pathkeys = build_index_pathkeys
useful_pathkeys = truncate_useless_pathkeys // 将索引的keypath和查询中的keypath做交集,如果有交集,则再生成逆序的扫描路径
if(useful_pathkeys != NIL) {
ipath = create_index_path(BackwardScanDirection) // 建立反向扫描的index
}
3. add_path的时候将带有pathkey的路径保存下来,主要代码流程如下所示:
add_path
costcmp = compare_path_costs_fuzzily
keyscmp = compare_pathkeys
// 如下通过case判断如果执行计划或者keypath 有一个占优势的执行路径都会被保留
三. min/max 的区别
min/max算子均走上述流程,其差异主要为当是min算子时候,build_index_paths中truncate_useless_pathkeys裁剪后的keypath为空,导致不会生成逆序扫描的执行计划。主要代码如下所示:
truncate_useless_pathkeys
pathkeys_useful_for_ordering
pathkeys_contained_in
compare_pathkeys
if (pathkey1->pk_strategy != pathkey2->pk_strategy) { // 因为索引的顺序是升序的,但是min算子对应的operator算子是降序的,因此才减掉此keypath,但是对于max算子,顺序是一致的,因此会保留此keypath
return PATHKEYS_DIFFERENT
}
if (nuseful == 0)
return NIL; // 返回空空将不会创建反序列的路径