文章导读:
谓词下推
常量折叠
常量传播
本文对SQL优化的三种常见规则进行了介绍,分别为谓词下推、常量折叠和常量传播。一起来看一下把~~对逻辑算子还没有概念的可以看这篇文章《SQL优化器执行过程之逻辑算子》,对于列裁减、投影消除等优化规则可以看上一篇文字《SQL优化规则之列裁减和投影消除》。
谓词下推
谓词下推是指将外层查询块where子句中的谓词移入所包含的较低层次的查询块,从而能够提前进行数据过滤以及更好的使用索引。
谓词下推是非常重要的一个优化规则,举个栗子:
select * from t1,t2 where t1.a > 3 and t2.b >5
。假设t1和t2都是100条数据。
如果我们不进行谓词下推优化,那么我们需要把t1和t2两个表做笛卡尔积,然后再根据条件进行过滤。也就是说我们需要处理10000条数据。而如果我们进行谓词下推之后,比如t1.a > 3
的数据有10条,t2.b > 5
的有5条,那么先进行过滤我们所需要处理的数据条数则只有50条了。这就是谓词下推的作用,我们尽量把过滤条件往下推到子节点上,就可以避免访问很多数据,从而达到优化的效果。
比如对于DataSource算子,直接将过滤条件推给各个DataSource算子即可。对于Join算子,则会首先进行简化,将外连接转化为内连接,收集连接条件,区分出哪些来自于Join的左节点哪些来自于Join的右节点,分别像左右节点进行下推。
需要注意的是谓词下推也是有边界的,不能推过Limit节点
先进行Limit n
再做Selection
操作和先做Selection
操作再Limit n
得到的结果是不一样的。
常量折叠
常量折叠(const folding
)是指在编译优化时,多个变量进行计算时,而且能够直接计算出结果,那么变量将由常量直接替换。
比如select * from table1 where a > 3*5
会转换为select * from table1 where a > 15
。
不光SQL优化中有常量折叠,在我们常用的编程语言中也是这样,比如
1void test() {
2 int a = 3 * 5;
3 printf("%d",a)
4}
5//通过常量折叠优化为
6void test() {
7 printf("%d",15)
8}
常量传播
常量传播,在编译优化时,将能够计算出结果的变量替换为常量
前面我们提到的常量折叠只关注了非常局部的信息,它处理不了变量被多次赋值的情况。比如:
1int x = 10;
2int y = x + 2;
3//此时从感觉上来说,我们会认为优化时应把x作为常量10,y应该实现常量折叠为12。
4
5int x = 10;
6x = random.nextInt(99);
7int y = x + 2
8//此时y就不能把x看作为一个常量了
所以,要实现常量传播,它必须要依赖一种叫做“到达定值”(reaching definition)的前向数据流分析(forward data-flow analysis)。它要确定某个定值能被传播到哪些使用点,或者反过来说,某个使用点上应该采用哪个版本的定值。如果在某个使用点上发现应该使用的定值是一个常量的话,就可以在此处做诸如常量折叠之类的常量优化了。
那么在SQL中,比如我们有这样一条语句:select * from table1 where a > 5 and a < 4
我们明眼一看就能看出来,不存在a > 5 && a < 4
的值,但是未经优化的SQL可不这么想,它会进行全表扫描。所以SQL优化就需要对a > 5
和a < 4
等条件进行条件常量传播,从而消除无用的节点,判断是否存在结果。
好了,常见的一些SQL优化规则我们就介绍到这里。后续则是常见大数据引擎的SQL优化器介绍,包括Calcite和Spark的Catalyst。
参考资料:
http://zenlife.tk/sql-optimizer4.md