SQL优化器解析|青训营笔记

643 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的第1天

SQL Optimizer 解析

SQL处理流程

在了解SQL优化器之前,我们首先要知道数据库执行SQL语句的处理流程:

  • 首先SQL语句进入解析器,被解析成 抽象语法树(Abstract Syntax Tree) 结构
  • 分析器 将AST转化为 逻辑计划(Logic Plan)
  • 逻辑计划经优化器输出为 物理计划(Physical Plan) 交由 执行器 执行

SQL-Optimizer-sql流程.jpg


解析器 Parser

涉及下面两个阶段

词法分析: 拆分字符串,提取关键字,字符串,数值等

语法分析: 把词条按照定义的语法规则组装成抽象语法树结构


分析器 Analyzer

访问库/表元信息并绑定判断SQL是否合理,当然最主要的是将 AST 转换成逻辑计划树


优化器 Optimizer

优化器可以说是数据库最复杂的模块,也是最重要的模块

一般 SQL 越复杂,需要连接的表就越多(比如在分布式的环境中),查询优化的意义就越大,因为不同执行方式的性能差别可能有成百上千倍


执行器 Executor

按照物理执行计划扫描和处理数据,充分利用机器资源(CPU 流水线,乱序执行,cache,SIMD)


逻辑计划树

简的来说,逻辑计算树是将SQL如何执行查询和计算的分步骤逻辑的(并没有明确实际的算法)用树来表示。

其中树的每一个节点代表一个算子,每一条边代表数据的流动方向


物理计划树

在单机 Plan 的基础上最小化数据移动最大化本地 Scan,生成 PlanFragment 树就是分布式物理计划的目标

每个 PlanFragment 可以在每个 executor 节点生成 1 个或多个执行实例,不同执行实例处理不同的数据集,通过并发来提升查询性能


查询优化

常见的查询优化器分为两种 RBO(Rule-based Optimizer)CBO(Cost-based Optimizer),即基于规则和基于代价的优化器。

查询的优化大多用到关系代数的等价交换(结合、交换、传递)


RBO(Rule-based Optimizer)

优化目标

  • IO
  • NetWork
  • CPU、Memory

基于规则的优化,是基于很多已有的经验,一般无法实现最优的优化


优化方法

现在用一个查询语句演示一下常见的RBO方法

SELECT t1.id,t2.name
FROM t1 JOIN t2
ON t1.id = t2.id AND t1.uid = t2.uid
WHERE t2.id > 99;

SQL-Optimizer-查询示例.jpg

上面这幅图显示初始的查询方案,先扫描两张表,然后连接、过滤、投影依次进行,显然这并不是一个很好的方案,我们后面需要对其进行一些简单的优化。

列裁剪:

列裁剪的方式主要是在扫描这一步,将扫描整张表的操作,替换成 只扫描所需要的属性 能够很大的减少开销,特别是对于大数据环境下的查询。

SQL-Optimizer-列裁剪.jpg

谓词下推:

谓词下推简的来说就是 将过滤操作Filter下推到靠近数据源的地方,让无用数据能尽早被过滤掉。

本例就可以将Filter操作移动到Join之前。

SQL-Optimizer-谓词下推.jpg

传递闭包:

传递闭包的原理是通过已有的条件,无中生有新的过滤条件来过滤数据

如示例查询中可以通过t1.id = t2.id 和 t2.id >99能够推出新的过滤条件t1.id>99

SQL-Optimizer-传递闭包.jpg

Runtime Filter

Runtime Filter旨在为某些 Join 查询 在运行时动态生成过滤条件,来减少扫描的数据量,避免不必要的I/O和网络传输,从而加速查询。

SQL-Optimizer-运行时过滤.jpg

运行时过滤采用的方法有很多这里简单说3个

min-max: 可以理解为使用范围过滤,但这种过滤要求查询的数据紧密,若数据分布松散导致范围仍然很大,这个过滤的意义就不大了。

in-list: 在构建Runtime Filter的一方值较少的时候可以采用in-list的方式,过滤的方式自然为扫描数据是否在list中。

bloom filter: 这种过滤方式时一种二进制向量数据结构,它具有空间和时间效率,被用来检测一个元素是不是集合中的一个成员。


CBO(Cost-based Optimizer)

执行计划预估代价,选择代价最小的方案

需要考虑的算子代价由:CPU、内存、磁盘IO、网络IO等

值得注意的是不同的系统计算代价的算法各异

graph LR;
a(统计信息 + 推导规则)--> b(计算算子代价) --> c(计算执行计划代价) --> d(执行计划枚举)

统计信息

  • 原始表统计信息
    • 表\分区
  • 推导统计信息
    • 选择率:返回数据的比例
    • 基数:需要处理的行数

统计信息的推导

要求列与列独立,列值均匀分布,有点类似概率的计算

  • AND条件: fs(a AND b)=fs(a) * fs(b)

  • OR条件: fs(a OR b)=fs(a) + fs(b) - fs(a) * fs(b)

  • NOT条件: fs(NOT a)=1 - fs(a)

  • 等于条件

    • 若值不在[min,max]范围内:0
    • 若值在[min,max]范围内:1/NDV
  • 小于条件

    • value<min:0
    • value>max:1
    • min<value<max:(value-min)/(max-min)

但是这样的推动与实际并不相符合,因为现实生活中的数据不一定符合均匀分布。


执行计划枚举

通常是采用贪心算法或者动态规划选出最优的执行计划