一. 前言
在openGuass中,如果对索引列执行max/min操作,openGauss会优化成只读取索引的最前/后的一行数据,避免了对整表数据进行读取和聚合操作,如下所示:
二. min/max优化代码走读
1. 首先需要将min/max 算子转成成执行计划中降序/升序的keypath,代码流程如下所示:
preprocess_minmax_aggregatesfind_minmax_aggs_walker // 从pg_aggregate中查出max/min对应的aggsortop,max对应的是>, min对应的是<get_equality_op_for_ordering_op //根据aggsortop到pg_amop表中查找对应的op信息,如amopstrategy等,对应是查询中是需要顺序扫描还是逆序扫描build_minmax_pathsortcl->eqop = eqop;sortcl->sortop = sortopparse->sortClause = st_make1(sortcl); // 已经将max/min聚合操作映射成排序操作,max对应的是逆序,min对应着是顺序parse->limitCount = makeConst(1) // 其实已经将执行计划中的max替换成了order by desc limit 1,min被替换成了 order by asc limit 1query_plannerconstruct_pathkeysconstruct_pathkeysmake_pathkeys_for_sortclausesmake_pathkey_from_sortop // 根据第一步拿到的aggsortop生成pathkeymake_pathkey_from_sortinfostrategy = reverse_sort ? BTGreaterStrategyNumber : BTLessStrategyNumbermakePathKey(strategy) 在执行计划中将min/max映射成的keypath信息保存下来
2. 生成索引路径的时候,根据keypath的信息生成顺序扫描路径还是逆序扫描路径,主要代码如下所示:
build_index_pathsif(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_pathcostcmp = compare_path_costs_fuzzilykeyscmp = compare_pathkeys// 如下通过case判断如果执行计划或者keypath 有一个占优势的执行路径都会被保留
三. min/max 的区别
min/max算子均走上述流程,其差异主要为当是min算子时候,build_index_paths中truncate_useless_pathkeys裁剪后的keypath为空,导致不会生成逆序扫描的执行计划。主要代码如下所示:
truncate_useless_pathkeyspathkeys_useful_for_orderingpathkeys_contained_incompare_pathkeysif (pathkey1->pk_strategy != pathkey2->pk_strategy) { // 因为索引的顺序是升序的,但是min算子对应的operator算子是降序的,因此才减掉此keypath,但是对于max算子,顺序是一致的,因此会保留此keypathreturn PATHKEYS_DIFFERENT}
if (nuseful == 0)return NIL; // 返回空空将不会创建反序列的路径