openLooKeng执行计划优化简介
在讲述下推框架以前,咱们先来简单介绍一下openLooKeng执行计划优化的大体流程。node
如上图所示,接收到用户SQL语句以后,SQL被转换成一个Abstract syntax tree (AST)树。AST树再被转换成逻辑执行计划树。而后,也就是执行计划优化过程当中最重要的一步,使用规则(Rule)或者优化器(Optimizers)进行执行计划优化,每个PlanOptimizer能够操做一个子执行计划树,PlanOptimizer基于统计或者经验,用一个更优的子执行计划替换当前的子树,达到优化的目的。PlanOptimizers一般是长期的经验累积得出来的一些优化规则,好比谓词下推、join reorder等等。PlanOptimizers能够存储一些物理执行信息在ConnectorHandle中。新下推框架则工做在这一层。mysql
获得最优的执行计划以后,逻辑执行计划被转换成物理执行计划,而后被分片,分割成按照stage执行的一个个子树,最终调度到worker上执行。git
PrestoSql下推方案介绍
最初,openLooKeng是从PrestoSql演进而来,在其基础上增长了不少功能特性,以及大量的性能优化。在讲述openLooKeng的新下推框架以前,先大概介绍其原下推方案。sql
- 根据社区的讨论,PrestoSql原下推框架有一些目标:
1)使用现有的Rule或Optimizer的框架,并且不是使用基于visitor模式的PlanOptimizers来实现下推,同时可以让connector提供转换rules来实现下推;数据库
2)并非创建一个原生的机制来支持全部操做的下推。性能优化
- 先来看看prestoSql的执行过程:
1)首先,引入一些列的下推规则,每个规则负责下推相应的操做到TableScan操做中,好比PushFilterIntoConnector, PushProjectionIntoConnector, PushAggregationIntoConnector等等;数据结构
2)上述的这些rules经过指定的metadata调用与connectors交互,若是connector支持这个操做下推,操做则被下推到TableScan操做,同时在connectorTableHadle中记录相关信息。框架
- 下面以PushFilterIntoConnector为例说明。
在上述的例子中,假设filter中有两个过滤条件,一个是like,一个是f函数,其中connector能处理like表达式。ide
PushFilterIntoConnector会调用 Metadata.pushFilter,其实是调用connector的pushFilter函数,这个函数会返回一个新的tableHandle,新的tableHandle中记录了like表达式的相关信息,同时返回一个remaining filter,即connector不能处理的表达式。最终,PushFilterIntoConnector就把原来的执行计划(上图中第一个框)转换成一个新的执行计划(上图中最后一个框)。函数
- 上述的基于Rule的下推方案存在如下的几个问题:
1)不能JoinNode,WindowNode等Nodes的下推,特别是 join的状况,join node不只仅须要visit当前的join node,还须要visit他的左右节点,同时,还须要保存join的上下文信息,基于rule的下推方案难以处理这种状况;
2)下推逻辑复杂,下推上下文信息没法保存,对于Join的状况,join的下推信息不知道存储在哪。
3) 基于上述缘由,当前的下推方案不能把sql语句下推到数据源操做中,这样对于那些执行速度至关快的数据源就不能充分发挥数据源自己的能力,因此引入了新的下推框架。
openLooKeng新的下推框架
- 思想
openLooKeng新下推框架的主要思想是把执行计划子树暴露给connector,让connector提供PlanOptimizers(基于visitor模式的)给执行优化引擎,这样可让connector引入任意的优化。
为了防止一个connector的PlanOptimizers修改其余connector的执行计划子树,openLooKeng对于暴露给Connector的PlanNode作了两个限制:
1)暴露出来的PlanNodes须移动到presto-spi模块;
2)仅仅暴露属于connector的子执行计划树给相应的connector。以下图所示,左子树只会暴露给Hive Connector,右子树只会暴露给Mysql Connector。而后会应用他们各自的PlanOptimizers。
- 实现
openLooKeng的下推框架如上图所示,新下推框架的工做原理很简单,主要分为两步:
1)Connector在启动的时候会告诉执行优化引擎其提供的ConnectorPlanOptimizer,以下图的HiveFpuPushdownOptimizer,其须要实现上图的optimize接口,optimize函数以子执行计划为入口,返回优化后的执行计划;
2)在执行优化引擎中引入ApplyConnectorOptimization优化器,
该Optimizer会把根据子执行计划所在的connector,调用其connectorPlanOptimizer。以下图所示,通过HiveFpuPushdownOptimizer优化以后,Aggregation和Filter操做都下推到了数据源中。
- 修改
新框架特性PR连接为
https://gitee.com/openlookeng/hetu-core/pulls/633,
主要作了以下修改:
-
移动PlanNodes到presto-spi模块
-
修改PlanNode和Assignments中的Expression为RowExpression(下一节描述)
-
添加TranslateExpressions 和 ApplyConnectorOptimization Optimizer
-
修改已经存在的Rules和Optimizers
-
为connector添加ConnectorPlanOptimizer
- Expression-to-RowExpression
在数据库或者查询系统中,为了更好的隔离,AST树和IR树是隔离,他们使用的数据结构也不同,这也就是presto社区讨论的分离AST(Node)和IR(PlanNode)。具体就是指把AST树种的Expression转换成PlanNode中的RowExpression。当前AST和PlanNode在混用Expression,不能作到很好的隔离。rker上执行。
当前一个执行计划的生命周期以下:
building AST
building raw plan
plan optimization
plan sanity check
plan cost computation
building subplans
distributing subplan (over the wire)
compiling subplan locally
当前,Expression到RowExpression的转换发生在第8步。咱们把转换这个操做移到了第3步。之因此没有把没有把转换移到第二步,是由于涉及的面太广了,修改量太大了。
Example 演示
首先,在演示系统中配置了三个catalog,他们都指向同一个数据源,不过下推的设置不同,mysql2不下推,mysql1部分下推(join不下推),mysql全下推。
不下推的状况
部分下推的状况,在这个例子中,filter下推了
所有下推状况
如何贡献
下面简单介绍一下开发者如何适配新的下推框架,即在新增的connector中,如何添加connectorPlanOptimizer。主要步骤以下:
第一:在XXXConnector中复写下面的函数
第二:实现XXXPlanOptimizerProvider
第三:在XXXConnector中实现PlanOptimizer
第四:实现PlanOptimizer里面的optimize函数,主要是实现一个visitor去visit执行计划树
第五:实现Visitor,用来生成下推的语句,同时修改执行计划树
第六:实现XXXQueryGenerator,在XXXQueryGenerator中实现一个visitor用来把下推的信息记录到XXXQueryGeneratorContext,若是存在节点能够下推,则生成对应的sql
详细的实现能够参考openLooKeng的baseJdbc的实现,实现了JDBC数据源的下推。
以上即是openLooKeng开发工程师罗旦带来的分享。
直播视频回顾:https://www.bilibili.com/video/BV1of4y1p7sc
活动官网:https://summer.iscas.ac.cn/
学生指南:https://summer.iscas.ac.cn/help/student/
任务详情:https://summer.iscas.ac.cn/#/org/orgdetail/openlookeng
openLooKeng是一款开源的高性能数据虚拟化引擎,提供统一SQL接口,具有跨数据源/数据中心分析能力,为大数据用户提供极简的数据分析体验。
openLooKeng开源社区官方网站: https://openlookeng.io/zh-cn/
openLooKeng代码仓地址: http://gitee.com/openlookeng