Presto之局部聚合和中间聚合计算

   一. 前言

       Presto的局部聚合是提升聚合计算效率的一个重要优化手段。其原理就是对于count,sum等聚合算子先在每一个split中完成局部聚合后再exchange到coordinator中完成最后的聚合计算。通过局部聚合计算可以大大减少聚合操作中数据的传输量从而提升最后的计算效率。

 

二. 局部聚合

     局部聚合是指计划执行树中的Aggregate(PARTIAL) 节点,如下图为其的一个样例:

       在Presto中,局部聚合算子的生成是通过PushPartialAggregationThroughExchange的优化器来实现,其实现思路是将一个最终聚合Aggregate(FINAL)经过PushPartialAggregationThroughExchange优化器的时候,附加一个 局部聚合算子Aggregate(PARTIAL),并且将附件的 Aggregate(PARTIAL)下推到Exchange算子底下实现的。其实现代码的走读如下:

private PlanNode split(AggregationNode node, Context context)
{
    .....
    entry是sql中的所有聚合操作算子,比如cout(a), max(b)等
    for (Map.Entry<Symbol, AggregationNode.Aggregation> entry : node.getAggregations().entrySet()) {
        // 地下主要获取到聚合操作的列和聚合算子
        AggregationNode.Aggregation originalAggregation = entry.getValue();
        String functionName = metadata.getFunctionAndTypeManager().getFunctionMetadata(originalAggregation.getFunctionHandle()).getName().getObjectName();
        FunctionHandle functionHandle = originalAggregation.getFunctionHandle();
        InternalAggregationFunction function = metadata.getFunctionAndTypeManager().getAggregateFunctionImplementation(functionHandle);
        Symbol intermediateSymbol = context.getSymbolAllocator().newSymbol(functionName, function.getIntermediateType());
        // order by做本地聚合会导致数据错乱,因为order by的操作的场景不进行优化,全部数据exchange到coordaintor处理
    checkState(!originalAggregation.getOrderingScheme().isPresent(), "Aggregate with ORDER BY does not support partial aggregation");
    
    // intermediateAggregation 就是partial Aggregation
    intermediateAggregation.put(
            intermediateSymbol,
            new AggregationNode.Aggregation(
                    new CallExpression(
                            functionName,
                            functionHandle,
                            function.getIntermediateType(),
                            originalAggregation.getArguments(),
                            Optional.empty()),
                    originalAggregation.getArguments(),
                    originalAggregation.isDistinct(),
                    originalAggregation.getFilter(),
                    originalAggregation.getOrderingScheme(),
                    originalAggregation.getMask()));

    // rewrite final aggregation in terms of intermediate function
    finalAggregation.put(
            entry.getKey(),
            new AggregationNode.Aggregation(
                    new CallExpression(
                            functionName,
                            functionHandle,
                            function.getFinalType(),
                            ImmutableList.<RowExpression>builder()
                                    .add(new VariableReferenceExpression(intermediateSymbol.getName(), function.getIntermediateType()))
                                    .addAll(originalAggregation.getArguments()
                                            .stream()
                                            .filter(PushPartialAggregationThroughExchange::isLambda)
                                            .collect(toImmutableList()))
                                    .build(),
                            Optional.empty()),
                    ImmutableList.<RowExpression>builder()
                            .add(new VariableReferenceExpression(intermediateSymbol.getName(), function.getIntermediateType()))
                            .addAll(originalAggregation.getArguments().stream()
                                    .filter(PushPartialAggregationThroughExchange::isLambda)
                                    .collect(toImmutableList()))
                            .build(),
                    false,
                    Optional.empty(),
                    Optional.empty(),
                    Optional.empty()));
}

// 生成本地聚合算子
PlanNode partial = new AggregationNode(...intermediateAggregation, PARTIAL...);

// 生成 Aggregate(FINAL)   --   Aggregate(PARTIAL)的计划树
return new AggregationNode(...partial....finalAggregation...);
}

       经过上边的split操作后,还需要通过PushPartialAggregationThroughExchange::pushPartial操作将PARTIAL Aggregate下推到exchange的地下完成整个计划执行树的聚合聚合优化。

三. 中间聚合

      在Presto中,还有一个中间聚合Aggregate(INTERMEDIATE)的聚合态,其优化目的是为了Final 聚合在送给coordaintor之前,再让worker做一次局部聚合的再聚合操作,从而减轻coordinator的计算压力和提升聚合计算的并行度。如下是一个中间聚合的计划执行树样例: 

       中间聚合优化在AddIntermediateAggregations优化规则中实现的。其实现原理是从计划执行树的上往下找到Final aggregation节点中的PARTIAL AggregationNode的Source节点,然后在PARTIAL AggregationNode Source中增加一个ExchangeNode 和INTERMEDIATE AggregationNode来完成计划执行树的优化。

      中间聚合默认是不开启的,需要通过enable_intermediate_aggregations将其开启。

  

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值