暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Calcite 之CBO框架

叁金大数据 2019-02-19
996

通过我们前面对Calcite的介绍,我们知道Apache Calcite 是一个独立于存储与执行的SQL优化引擎,广泛应用于开源大数据计算引擎中,如Flink、Drill、Hive、Kylin等。Calcite的架构如图所示:


图中的Operator Expressions
指的就是关系表达式即查询计划,一个关系表达式在Calcite中被表示为RelNode
(Calcite介绍时代码中有提到过),往往以根节点代表整个查询树。从图中我们可以看到Operator Expressions
的来源有两处,分别时SQL Parser
Expressions Builder

SQL Paser And Validator

Calcite自身提供了Parser用于SQL解析,是基于JavaCC实现的,直接使用Parser就能得到RelNode Tree。有兴趣的话可以看一下JavaCC相关的语法,在Calcite中Drop Table
的解析实现大概就是这样的:

 1SqlDrop SqlDropTable(Span s, boolean replace) :
2
{
3    SqlIdentifier name;
4    boolean ifExists = false;
5}
6{
7    <TABLE>
8    [
9        (<IF> <EXISTS>) { ifExists = true; }
10    ]
11    name = CompoundIdentifier()
12    {
13        return new SqlDropTable(s.end(name), name, ifExists);
14    }
15}

复制

Expressions Builder

不同系统语法有差异,所以Parser也可能不同。针对这种情况,Calcite提供了Expressions Builder来对抽象语法树(或其他数据结构)进行转换得到RelNode Tree。如Hive(某一种Data Processing System)使用的就是这种方法。

Query Optimizer

终于到我们的重头戏了,Query Optimizer
根据优化规则(Pluggable Rules
)对Operator Expressions
进行一系列的等价转换,生成不同的执行计划,最后选择代价最小的执行计划,其中代价计算时会用到Metadata Providers
提供的统计信息。所以Query Optimizer
就是Calcite CBO的核心实现。

比如我们有这样一条SQL语句:

1INSERT INTO test_table
2SELECT t1.id, t1.name, t2.val
3FROM table1 as t1 INNER JOIN table2 AS t2
4ON t1.id = t2.id and t1.name = t2.name where t1.val > 5 and t2.val = 3

复制

通过Calcite生成的未经优化的RelNode树如下:

1LogicalTableModify(table=[[TEST_TABLE]], operation=[INSERT], flattened=[false])
2  LogicalProject(ID=[$0], NAME=[$1], VAL=[$7])
3    LogicalFilter(condition=[AND(>($25), =($83))])
4      LogicalJoin(condition=[AND(=($0, $5), =($1, $6))], joinType=[INNER])
5        LogicalTableScan(table=[[TABLE1]])
6        LogicalTableScan(table=[[TABLE2]])

复制

结合我们前面提到的逻辑算子,可以发现最底层是TableScan,也是读取表的原始数据,紧接着是LogicalJoin,Joiner的类型为INNER JOIN, LogicalJoin之后接下做LogicalFilter 操作,对应SQL中的Where条件,最后做LogicalProject也就是投影操作。

根据我们前面了解的优化规则我们知道对于INNER JOIN
而言是可以进行谓词下推的,那么优化后的RelNode如下:

1LogicalTableModify(table=[[TEST_TABLE]], operation=[INSERT], flattened=[false])
2  LogicalProject(ID=[$0], NAME=[$1], VAL=[$5])
3      LogicalJoin(condition=[AND(=($0, $3), =($1, $4))], joinType=[inner])
4        LogicalFilter(condition=[=($23)])  
5          LogicalProject(ID=[$3], NAME=[$4], VAL=[$5])
6            LogicalTableScan(table=[[TABLE2]])
7        LogicalFilter(condition=[>($2,5)])    
8          LogicalProject(ID=[$0], NAME=[$1], VAL=[$2])
9          LogicalTableScan(table=[[TABLE1]])

复制

当然,上面的Logical Tree
只是简单的示例,由此也能看出Optimizer
主要目的就是减小SQL所处理的数据量、减少所消耗的资源并最大程度提高SQL执行效率。所使用的方法就是我们前面提到了SQL优化规则如:裁剪掉无用的列、合并投影、子查询转化成JOIN、JOIN重排序、下推投影、下推过滤等等。

总结一下,Calcite会将每一种操作(如LogicaJoin
LocialFilter
、 LogicalProject
LogicalScan
) 结合实际的Schema转化成具体的代价数,比较不同的执行计划所具有的代价,然后选择相对小计划作为最终的结果,之所以说相对小,这是因为如果要完全遍历计算所有可能的代价可能得不偿失,花费更多的人力与资源,因此只是说选择相对最优的执行计划。

CBO目的是“避免使用最差的执行计划,而不是找到最好的“

参考资料:

https://zhuanlan.zhihu.com/p/40478975

https://www.jianshu.com/p/2dfbd71b7f0f

往期精彩回顾
SQL优化器执行过程之逻辑算子
Calcite 项目及使用介绍
SQL优化器简介


扶我起来,我还能更新~



文章转载自叁金大数据,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论