Greenplum查询优化揭秘
学习地址:https://space.bilibili.com/489184136
1 Greenplum查询优化器和查询计划介绍
1.1 Greenplum查询优化器介绍
对于给定的查询语句,找到”代价”最小的查询计划的顺序
1、对于同一个查询语句,一般可以由多种方式执行
2、查询优化器尽可能去遍历每一种可能的执行方式
3、找到”代价”最小的执行方式,并把它转化为可执行的计划树
1.2 Greenplum查询计划介绍
1、一个查询计划就是一个由计划节点组成的树
2、每个计划节点代表了一个特定类型的处理操作,计划节点中包含了执行器执行所需要的全部信息
3、在执行时,计划节点产生输出元组
4、一般来说,扫描节点从数据表中获取输入元组
5、大部分其他节点层他们的子计划节点中获取输入元组,并产生输出元祖
1.3 计划节点的类型
1、扫描节点
顺序扫描,索引扫描,位图扫描
2、链接节点
Nestloop,hash,merge
3、非SPJ节点
Sort,aggregate,set operations(UNION etc)
扫描节点实例
2 Greenplum查询优化器的的具体处理过程
2.1 查询树的预处理
尽可能的简化查询树,收集有用的信息
2.1.1 查询树的预处理(早期)
2.1.1.1 简化常量表达式
1、简化函数表达式
函数本身是”严格”的,并且输入包含NULL值,示例如下
Int4eq(1,NULL) => NULL
函数本身是”IMMUTABLE”的,并且输入参数全都常量
2 + 2 => 4
2、简化常量表达式
简化布尔表达式
“x or true” => “true”
“ x AND false ” => “false”
3、简化CASE表达式
CASE WHEN 2+2 = 4 THEN x+1
ELSE 1/0 END
= > x + 1
... not “ERROR:division by zero”
4、为什么简化常量表达式
A、仅需要做一次计算,而不是为每行元组都做一次计算
B、视图展开和函数内连都可能会带来新的常量表达式简化的机会
C、简化常量表达式也为统计信息类的函数减少了计算量
2.1.1.2内连简单的SQL函数
create function incr4(int) returns int as ‘select $1 + ( 2 + 2 ) ’ LANGUAGE SQL;
select incr4(a) from foo;
== >
Select a + 4 from foo;
为什么使用内联简单的SQL函数
1、避免SQL函数调用的代价
2、为简化常量表达式提供新的机会
2.1.1.3 提升IN,EXISTS类型的子链接
子链接是指吃现在表达式中的子查询,通常出现在where或join/on子句中
select * from foo where exists (select 1 from bar where foo.a = bar.c);
== >
Select * from foo *SEMI JOIN* bar on foo.a = bar.c
查询树的内部结构图
优化后的内部结构图
2.1.1.4 提升子查询
子查询一般以范围表的方式存在,通常出现在from字句中
select * from foo join (select bar.c from bar join baz on true) as sub on foo.a = sub.c;
==>
select * from foo join (bar join baz on true) on foo.a = bar.c;
提升之前的查询计划图
提升之后的子查询计划图
为什么提升子查询
1、通过把子查询提升到父查询之中,就可以使子查询参与整个计划搜索空间,从而找到更好的执行计划。
2、否则,我们不得不为了子查询单独做计划树,然后在为父查询做计划时把子查询当做是一个”黑盒子”
2.1.1.5消除外链接
消除外链接的实例
外链接的上层有”严格”的约束条件,且该条件限定了来自nullable side的某一变量为非null值,则外链接可以转化成内连接
select ... from foo left join bar on (...) where bar.d = 42;
==>
select ... from foo inner join bar on (...) where bar.d = 42;
2.1.2 查询树的预处理(后期)
2.1.2.1 分发where和join/on约束条件
1、一般来说,我们期望可以尽可能的下推约束条件
2、如果只有内连接,我们可以把一个约束条件下推到它的”自然语义”位置
3、如果存在外链接,那么约束条件的下推可能会受到阻碍,从而无法下载到它的“自然语义”的位置
4、对于被外连接阻碍的约束条件,我们通过让他们的“required_relids”包含进外链接锁需要的所有基表,从而避免该约束条件被下推到外链接之下
被外链接阻碍的约束条件案例
2.1.2.2 收集关于链接顺序限制的信息
1、外链接会在一定程度上限制连接顺序的交换
2、非FULL-JOIN可以和一个外链接的左端(LHS)自由结合
3、通常非FULL-JOIN不可以和外链接的有段(RHS)结合
2.1.2.3 消除无用链接
1、必须是做链接,且内表是基表
2、内表的列没有在该连接之上上使用
3、连接条件最多只可能匹配内表中的一个元组
消除无用链接实例
2.2 扫描/链接优化
为查询语句中扫描和链接部分做计划,实例如下:
1、首先为基表确定扫描路径,估计扫描路径的代价和大小
2、利用动态规划算法,搜索整个链接顺序空间,生成链接路径
3、在搜索链接顺序空间是,需要考虑到由外链接带来的链接顺序的限制
2.3 动态规划
1、为每一个基表生成扫描路径
2、为所有可能的两个表的链接生成链接路径
3、为所有可能的三个表的链接生成链接路径
4、为所有可能的四个表的链接生成链接路径
*****
5、直到所有基表都连接在了一起
2.4 扫描/连接之外的优化
为查询语句中扫描和链接之外的部分做计划,扫描/连接之外的优化的步骤如下:
1、首先为表确定扫描路径,估计扫描路径的代价和大小
2、利用动态规划算法,搜索整个链接顺序空间,生成链接路径
3、在搜索链接顺序空间时,需要考虑到由外链接带来的链接顺序的限制
4、处理GROUP BY ,aggregation,windom,functions,DISTINCT
5、处理集合操作,UNION/INTERSECT/EXCEPT
6、如果ORDER BY需要,添加最后的SORT节点
7、添加LockRows,Limit,ModifyTable节点
1、主要处理查询语句中FROM和WHERE部分
2、同时也会考虑到ORDER BY信息
3、有代价来驱动
2.5 计划树的后处理
把优化结果转化成执行器可以执行的形式
1、把代价最小的路径转化成计划树
2、调整计划树中的一些细节,包含以下步骤
1)、展平子查询的范围表
2)把上层计划节点中的变量变成OUTER_VAR或时INNER_VAR的形式,来指向自己花的输出
3)删除不必要的SubqueryScan,Append等节点