一. 背景
本文通过调试“SELECT * FROM (SELECT custkey, orderkey, rank() OVER (PARTITION BY custkey ORDER BY orderdate ASC FROM orders ) WHERE custkey > 100” 在PredicatePushDown中的执行流程来理解在Presto中谓词下推是如何实现的。该SQL优化前的执行计划树如下:

二. PredicatePushDown的谓词下推实现
step1:
首先PredicatePushDown会访问顶层节点OutputNode,因为PredicatePushDown::Rewriter没有实现visitOutputNode方法,因此调用了visitPlan方法。visitOutputNode优化之前的计划执行树如下:

step2:
step1中的visitPlan的context.defaultRewrite方法,会递归rewrite OutputNode的source节点,因此访问OutPutNode之前,会先调用了PredicatePushDown:Rewriter::visitFilter访问FilterNode,viistFilterNode优化之前的计划执行树:

step3:
step2中的visitFilter的context.rewrite方法,会递归rewrite FilterNode的source节点,因此访问FilterNode之前,先调用了PredicatePushDown:Rewriter::visitProject访问ProjectNode,visitProjectNode优化前的计划执行树:

step4:
step3中的visitProject的context.defaultRewrite方法,会递归rewrite ProjectNode的source节点,因此访问ProjectNode之前,先调用了PredicatePushDown:Rewriter::visitWindow访问WindowNode,visitWindowNode优化前的计划执行树:

step5:
step4中的visitWindow的context.defaultRewrite方法,会递归rewrite WindowNode的source节点,因此访问WindowNode之前,先调用了PredicatePushDown:Rewriter::visitTableScan访问TableScanNode,visitTableScanNode优化前的计划执行树:

step6:
visitTableScan的时候通过如下代码插入了一个FilterNode,实现了Filter下推:
public PlanNode visitTableScan(TableScanNode node, RewriteContext<Expression> context)
{
Expression predicate = simplifyExpression(context.get());
if (!TRUE_LITERAL.equals(predicate)) {
return new FilterNode(idAllocator.getNextId(), node, predicate);
}
return node;
}
step7:
visitTableScan优化后的执行计划树:

step8:
递归返回step4的visitWindow,visitWindow会用优化后的source替换优化前的source节点,替换优化后source后的执行计划树:

step9:
递归返回step3的visitProject,visitProject会用优化后的source替换优化前的source节点,替换优化后source后的执行计划树:

step10:
递归返回step2的visitFilter,visitFilter中发现优化后,直接用sourse节点替换原来的执行计划树,因为visitFilter后的执行计划树:

step11:
递归返回step1的visitOutput,visitOutput会用优化后的source替换优化前的source节点,替换优化后source后的执行计划树:

step12:
因此,经过PredicatePushDown优化后的计划执行树如下所示,实现了谓词下推。

本文详细解析了Presto中PredicatePushDown的执行流程,通过一步步展示如何从TableScan开始,通过递归重构执行计划树实现谓词下推,最终优化SQL执行效率。
834

被折叠的 条评论
为什么被折叠?



