查看原文
其他

Hive优化器原理与源码解析系列--优化规则HiveAggregateProjectMergeRule(十六)

后羿BigDataplus BigDataplus 2021-10-15

目录


背景

优化规则HiveAggregateProjectMergeRule

  • matches方法逻辑详解

  • onMatch方法逻辑详解

  • apply方法等价变换的具体过程详解

总结


背景

        这篇文章来讲优化规则HiveAggregateProjectMergeRule,主要功能是将Project投影操作之上的Aggregate聚合函数操作两者进行合并,前提是只有当聚合函数的GroupBY分组表达式和参数是字段引用(即,不是表达式)时,才满足优化规则使用条件。如果识别到Project上的Aggregate操作,如果是通过Project做的汇总,进行两者合并或将Project移除,即group by 字段和投影字段相同,将两者合并。在某些情况下,此规则具有修剪的效果:聚合将使用比Projetct投影操作更少的列。

        在CalciteAPI中关于构建Aggregate汇总操作对象组成元素。它与SQL查询语句中的GROUPBY运算符以及SELECT子句中的聚合函数相对应。

  • Aggregate汇总:

protected Aggregate(RelOptCluster cluster, RelTraitSet traitSet, RelNode input, ImmutableBitSet groupSet, java.util.List<ImmutableBitSet> groupSets, java.util.List<AggregateCall> aggCalls)参数说明:cluster - 当前集群,提供执行环境traitSet - 特征集合input - 输入RelNodegroupSet - GroupSet变量groupSets - GroupSets元素列表aggCalls - 调用汇总函数的集合

        说明:groupSets的所有成员都必须是groupSet的子集。对于简单的GROUP BY,groupSets是一个包含groupSet的单例列表。如果未指定GROUP BY,或者如果指定GROUP BY(),则groupSet将为空集,并且groupSets将有一个元素,即该空集。如果指定了多维数据集、汇总集或分组集,则groupSet将有其他元素,但每个元素都必须是groupSet的一个子集,并且必须按包含进行排序:(0,1,2),(1),(0,2),(0),()。

  • Project投影:从输入RelNode中计算一组“SELECT EXPRESSIONS”的关系表达式。


优化规则HiveAggregateProjectMergeRule

1)matches方法逻辑详解

        matches方法返回此规则Rule是否可能与给定的操作数operands匹配,但是此方法的任何实现都可以给出误报,也就是说虽然规则与操作数匹配,但随后具OnMatch(ReloptRuleCall)而不生成任何后续任务。

判断由RelOptCall调用的优化规则Rule是否与输入参数RelNode关系表达式匹配,即此优化规则Rule能否应用到一个RelNode关系表达式树上。

        如果此表达式,含有GroupId,这条规则不能应用,因为GroupId的变化,Value也会发生改变

@Overridepublic boolean matches(RelOptRuleCall call) { final Aggregate aggregate = call.rel(0); //获取root根节点表达式 for (AggregateCall aggCall : aggregate.getAggCallList()) { if (aggCall.getAggregation().equals(HiveGroupingID.INSTANCE)) {//判断是否含有GroupID return false; } } return super.matches(call);}

Group_ID是group_sets集合中分组ID(类似排列组合的分组ID,1组、2组、3组等)。下面例子会使用group_sets和GROUPINGID进行查询,其中的 GROUPINGID,表示结果属于哪一个分组集合。

例如:

SELECT month,day,COUNT(DISTINCT cookieid) AS uv,GROUPING__IDFROM tab_testGROUP BY month,dayGROUPING SETS (month,day,(month,day))ORDER BY GROUPING__ID;
结果:month day uv GROUPING__ID------------------------------------------------2015-03 NULL 5 12015-04 NULL 6 1NULL 2015-03-10 4 2NULL 2015-03-12 1 2NULL 2015-04-12 2 22015-03 2015-03-10 4 32015-03 2015-03-12 1 32015-04 2015-04-12 2 3
等价于SELECT month,NULL,COUNT(DISTINCT cookieid) AS uv,1 AS GROUPING__ID FROM tab_test GROUP BY monthUNION ALLSELECT NULL,day,COUNT(DISTINCT cookieid) AS uv,2 AS GROUPING__ID FROM tab_test GROUP BY dayUNION ALLSELECT month,day,COUNT(DISTINCT cookieid) AS uv,3 AS GROUPING__ID FROM tab_test GROUP BY month,day说明:grouping sets 只会根据,sets集合内每个元素单独分组:month、day、(month,day)三个分组注意:group by中字段集合 要 包含 grouping sets()集合字段,否则会报错,即{group by} >={grouping sets}


2)onMatch方法逻辑详解

        接收有关一条规则匹配的通知。同时此方法被调用,call.rels保存了与规则Rule的操作数Operands匹配上的关系表达式RelNode集合;call.rels[0]是根表达式。通常一条规则Rule会检查这些节点是否有效匹配,创建一个新表达式RelNode(等价的)然后调用RelOptRuleCall.transformTo(org.apache.calcite.rel.RelNode, java.util.Map<org.apache.calcite.rel.RelNode, org.apache.calcite.rel.RelNode>)注册表达式。而RelOptRuleCall用一系列RelNode关系表达式集合作为参数,对RelOptRule优化规则的调用。

        call.rel(1)获取Project投影操作,call.rel(0)也即获取的Project操作之上Aggregate操作。apply函数将Project投影操作之上的Aggregate聚合函数操作两者进行合并的关键,返回优化后的非空的RelNode,RelOptRuleCall调用转换方法注册到RelSet集合,以备优化器构建最优执行计划。

@Overridepublic void onMatch(RelOptRuleCall call) { final HiveAggregate aggregate = call.rel(0);// 根表达式root expression 为Aggregate final HiveProject project = call.rel(1); //下一个表达式为Project RelNode x = apply(aggregate, project); //两个操作应用到一个RelNode if (x != null) { call.transformTo(x);//调用转换 }}


3)apply方法涉及到等价变换的具体过程

     传入参数为Aggregate操作对象和Project投影操作对象

public static RelNode apply(HiveAggregate aggregate, HiveProject project)

RexInputRef:引用输入关系表达式RelNode的字段的变量。

输入的字段是基于0的。如果有多个输入,则它们将连续编号。如果连接的输入是如下:

RexInputRef:(序号,字段数据类型)代表 一个字段
* Input #0: EMP(EMPNO, ENAME, DEPTNO) and* Input #1: DEPT(DEPTNO AS DEPTNO2, DNAME) 字段分别如下:
* Field #0: EMPNO* Field #1: ENAME* Field #2: DEPTNO (from EMP)* Field #3: DEPTNO2 (from DEPT)* Field #4: DNAME

因此 RexInputRef(3, Integer) is 字段 DEPTNO2的正确的引用.

  1. 初始化groupset字段索引与投影中字段索引的映射关系,并判断Project投影的行表达式,是一个字段的引用,而不是函数表达式,否则将无法应用此优化。

for (int key : aggregate.getGroupSet()) {//Returns a bit set of the grouping fields ( 如上述:grouping sets(cur_stt,crt_tim) ) final RexNode rex = project.getProjects().get(key); //project.getProjects()返回类型:List<RexNode> //select 1,2,sum(a) from t group by 1,2 if (rex instanceof RexInputRef) { //判断Project投影的行表达式,是一个字段的引用,而不是函数之类的 final int newKey = ((RexInputRef) rex).getIndex(); //取出字段引用的Ref的字段序号。 newKeys.add(newKey); map.put(key, newKey); //初始化groupset字段索引与投影中字段索引的映射关系 } else { // Cannot handle "GROUP BY expression" return null; }}

2 .遍历调用汇总函数,函数列表,判断AGG引用的字段是否在Project投影中引用,而且是字段引用,而不是表达式的引用,否则将跳出优化。

for (AggregateCall aggregateCall : aggregate.getAggCallList()) {//遍历调用汇总函数,函数列表 final ImmutableList.Builder<Integer> newArgs = ImmutableList.builder(); for (int arg : aggregateCall.getArgList()) {//遍历 每个汇总函数内的参数,并到投影中确认,判断是否引用到字段,并添加到newArgs列表中,否则返回为null final RexNode rex = project.getProjects().get(arg); // 如果在Project投影中,没有找到则返回null或返回的不是字段引用,最终结果返回null,则会跳出优化 if (rex instanceof RexInputRef) { newArgs.add(((RexInputRef) rex).getIndex()); } else { // Cannot handle "AGG(expression)" return null; } }

3. 如果groupset顺序不同,或者包含重复,则添加一个Project。判断这两个列表是否相等,如果不相等,则进行遍历newKeys索引,并查找对应newGroupSet索引位置,添加到postList中。使用new Aggregate和posList列表创建一个new Project投影。这里完成了Aggregate和Project合并的操作作为一个RelNode。

RelNode rel = newAggregate; if (!newKeys.equals(newGroupSet.asList())) { //判断这两个列表是否相等,如果不相等,则进行遍历newKeys索引,并查找对应newGroupSet索引位置,添加到postList中。 final List<Integer> posList = Lists.newArrayList(); for (int newKey : newKeys) { posList.add(newGroupSet.indexOf(newKey)); } if (aggregate.indicator) {//如果indicator为true for (int newKey : newKeys) { posList.add(aggregate.getGroupCount() + newGroupSet.indexOf(newKey));//在分组字段个数的基础上+索引 } } for (int i = newAggregate.getGroupCount() + newAggregate.getIndicatorCount(); i < newAggregate.getRowType().getFieldCount(); i++) { posList.add(i); } rel = HiveRelOptUtil.createProject( HiveRelFactories.HIVE_BUILDER.create(aggregate.getCluster(), null),        rel, posList);// 这里合并最要的一步:使用new Aggregate和posList列表创建一个new Project投影。这里完成了Aggregate和Project合并的操作作为一个RelNode。 }
return rel;}


总结

        优化规则HiveAggregateProjectMergeRule是将Project投影和Aggregate汇总参数及GroupBy引用字段(注,不能是表达式)相同,会将Aggregate和Project进行合并。

        由于笔者知识及水平有限,因此文中错漏之处在所难免,恳请各位老师、专家不吝赐教。



往期文章分享


优化规则系列

Hive优化器原理与源码解析系列--优化规则SortRemoveRule(一)

Hive优化器原理与源码解析系列--优化规则SortJoinReduceRule(二)

Hive优化器原理与源码解析系列--优化规则SortProjectTransposeRule(三)

Hive优化器原理与源码解析系列--优化规则SortUnionReduceRule(四)

Hive优化器原理与源码解析系列--优化规则SortMergeRule(五)

Hive优化器原理与源码解析系列--优化规则ProjectFilterPullUpConstantsRule(六)

Hive优化器原理与源码解析系列--优化规则SortLimitPullUpConstantsRule(七)

Hive优化器原理与源码解析系列--优化规则UnionPullUpConstantsRule(八)

Hive优化器原理与源码解析系列--优化规则ProjectOverIntersectRemoveRule(九)

Hive优化器原理与源码解析系列--优化规则ProjectSortTransposeRule(十)

Hive优化器原理与源码解析系列--优化规则HiveProjectMergeRule(十一)

Hive优化器原理与源码解析系列--优化规则HiveJoinAddNotNullRule(十二)

Hive优化器原理与源码解析系列--优化规则HiveJoinCommuteRule(十三)

Hive优化器原理与源码解析系列--优化规则PartitionPruneRule(十四)

Hive优化器原理与源码解析系列--优化规则HivePreFilteringRule(十五)

成本模型系列

Hive优化器原理与源码解析系列—统计信息带谓词选择率Selectivity

Hive优化器原理与源码解析系列—统计信息之选择性

Hive优化器原理与源码解析系列—统计模块内存成本估算

Hive优化器原理与源码解析系列--统计信息中间结果大小计算

Hive优化器原理与源码解析系列—CBO成本模型CostModel(一)

Hive优化器原理与源码解析系列—CBO成本模型CostModel(二)

Hive优化器原理与源码解析系列—统计信息UniqueKeys列集合

Hive优化器原理与源码解析—统计信息Parallelism并行度计算

Hive优化器原理与源码解析—统计信息NDV唯一值数估算


: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存