这是我参与「第四届青训营 」笔记创作活动的第 2 天。
一、笔记内容
- 大数据体系和 SQL;
- 常见的查询优化器;
- 查询优化器的社区开源实践;
- SQL 相关的前沿趋势。
二、大数据体系和 SQL
1.大数据体系中的SQL
SQL 与大数据体系中的分析引擎部分有密切关系。分析引擎的各种实现工具,如Hive、Flink等,都涉及数据查询处理,此时SQL的优化对系统的性能提升有至关重要的作用。
graph TD
大数据体系 --> 权限管控
权限管控 --Apache Range--> 集群创建
权限管控 --GDRP--> 集群创建
大数据体系 --> 数据开发
数据开发 --Airflow--> 集群创建
数据开发 --DAG--> 集群创建
大数据体系 ---> 基础设施
基础设施--ECS--> 日志查询
基础设施--存储--> 日志查询
基础设施--VPC--> 日志查询
基础设施--NSQ--> 日志查询
大数据体系 ----> 资源调度
资源调度 --YARN--> 用户管理
资源调度 --K8S--> 用户管理
资源调度 --Kafka--> 用户管理
大数据体系 ------> 存储系统
存储系统 --HDFS--> 监控报警
存储系统 --HBase--> 监控报警
存储系统 --NAS--> 监控报警
存储系统 --Object Store--> 监控报警
存储系统 --数据湖--> 监控报警
存储系统 --Pulsar--> 监控报警
大数据体系 -------> 业务应用
业务应用 --BI报表--> 管控运维
业务应用 --数据挖掘--> 管控运维
业务应用 --精准推荐--> 管控运维
业务应用 --营销分析--> 管控运维
大数据体系 ---------> 分析引擎
分析引擎 --> 批式分析
批式分析 --Spark--> 集群管理/服务管理
批式分析 --Hive--> 集群管理/服务管理
批式分析 --MR--> 集群管理/服务管理
分析引擎 --> 实时分析
实时分析 --Flink--> 集群管理/服务管理
分析引擎 --> 交互分析
交互分析 --Presto--> 集群管理/服务管理
交互分析 --ClickHouse--> 集群管理/服务管理
交互分析 --Doris--> 集群管理/服务管理
分析引擎 --> 消息队列
消息队列 --Kafka--> 集群管理/服务管理
消息队列 --Pulsar--> 集群管理/服务管理
消息队列 --NSQ--> 集群管理/服务管理
2.SQL的处理流程
1.Parser
-
文本 --> AST(抽象语法树结构)
-
词法分析阶段 --> 拆分字符串,提取关键字,字符串,数值等;
语法分析阶段 --> 把词条按照定义的语法规则组装成AST。
实现:递归下降(ClickHouse)、Flex、Bison(PostgreSQL)、javaCC(Flick)、Antlr(Presto,Spark)
2.Analyzer
1.访问库/表元信息并绑定;
2.判断 SQL 是否合理;
3.AST --> Logical Plan(逻辑计划树)
3.AST 和 Logical Plan 逻辑计划树
这里我把AST和逻辑计划树放在一起总结,是因为我觉得这样比较好理解。
graph TD
select语句 --> SELECT的字段
select语句 --> FROM的表
select语句 --> WHERE的条件
WHERE的条件 --> Between
WHERE的条件 --> In
WHERE的条件 --> Like
WHERE的条件 --> ...
select语句 --> GROUP的字段
select语句 --> ORDER的字段
上图简单表达了一个SQL查询语句的结构,其实它就是一个AST,将文本的SQL转换为其结构表达图,我们把它叫做抽象语法树。 看到这里,其实大家应该考虑一个问题,它们是同时执行还是按一定的顺序执行呢?这对后文的理解比较重要。
SQL的select查询语句是按照一定的顺序执行的!!
SQL查询语句的执行顺序一般为:
select 5
..
from 1
..
where 2
..
group by 3
..
having 4
..
order by 6
..
下列一个SQL查询语句:
SELECT
country.name, SUM(weblog.bytes) as total
FROM
country
inner join geoip on country.id = geoip.country_id
inner join weblog on geoip.host = weblog.host
WHERE
weblog.reply = "200" and weblog.host is not null
GROUP BY
country.name
ORDER BY
total
LIMIT 10;
其实我们可以从SQL是执行顺序来理解 Logical Plan,让我们对照下图从下往上,从左往右看 。
1.在执行SQL语句时,首先执行FROM语句,此时扫描(SCAN)country表和geoip表,并对它们按条件进行连接。至于weblog表,我们可以发现谓词下推的踪迹,因为原本应该是先连表再过滤,但weblog表和geoip表的连接明显推迟了。
2.WHERE经过谓词下推已经提前完成了。
3.group by 聚合操作,一般是根据字段值进行分组。
4.having过滤语句此SQL语句中没有出现,但它一定是在聚合之后才可以操作。
5.select 语句在此图中没有体现,但想象一下,没有查出来数据,如何排序。
6.order by语句,对查询出的数据进行排序操作,提高数据的可读性。
7.limit语句,成百上千条数据在显示时,肯定要进行分页,这个语句控制显示器每页显示的记录数。(limit没有给它排序,是因为它是MySQL特有的语句)
4.Optimizer 查询优化
查询优化的原因 --> SQL 是一种声明式语言,用户只描述做什么,没有告诉数据库怎么做。
查询优化的目标 --> 为 SQL 找到一个正确的且执行代价最小的执行计划。
查询优化的不确定性 --> 查询优化器是数据库的大脑,最复杂的模块,很多相关问题都是 NP(非确定性)的,很难找到一个最优解。
查询优化的意义 --> 一般 SQL 越复杂,Join 的表越多,数据量越大,使用不同执行方式来大幅度提高性能的可能性就越大。
5.Physical Plan 物理执行计划
优化器的输出 --> 分布式的物理执行计划。
Physical Plan 的目标 --> 最小化网络数据传输。即在单机 Plan 的基础上最小化数据移动和最大化本地 Scan,生成 PlanFragment 树。
Physical Plan 的实现 --> 利用数据上的物理分布(数据亲和性)。一个 PlanFragment 封装了在一台机器上对数据集的操作逻辑。每个 PlanFragment 可以在每个 executor 节点生成 1 个或多个执行实例,不同执行实例处理不同的数据集,通过并发来提升查询性能。
Plan 分布式化的方法 --> 增加 shuffle 算子,执行计划树会以 shuffle 算子为边界拆分为PlanFragment。
6.Executor 执行器
Executor 按照物理执行计划扫描和处理数据,充分利用机器资源(CPU 流水线,乱序执行,cache,SIMD),分为两种:
- 单机并行:cache,pipeline,SIMD
- 多机并行:一个fragment对应多个实例
三、常见的查询优化器
在介绍逻辑计划树时,我们提到了谓词下推,谓词下推属于查询优化器的一种实现方式,下面我们就对查询优化器的相关知识进行总结。
1.查询优化器的分类
graph TD
查询优化器的分类 --> Top-down
查询优化器的分类 --> Bottom-up
查询优化器的分类 --> RBO
查询优化器的分类 --> CBO
2.RBO
Rule-based Optimzer(RBO) :根据关系代数等价语义,重写查询;基于启发式规则;会访问表的元信息,不会涉及具体表数据。
1.优化的基础:关系代数
- 运算符 --> select,project,join,Renam,Union等;
- 等价变换 --> 结合律、交换律、传递性。
2.优化的原则:I/O、Network、CPU & Memory
- I/O --> Read data less and faster.
- Network --> Transfer data less and faster.
- CPU & Memory --> Process data less and faster.
3.优化的方法:列裁剪、谓词下推、传递闭包、Runtime Filter
主流RBO实现一般都有几百条基于经验所得到的优化规则。
- 列裁剪 --> 裁剪搜索树中不需要的列表信息。
- 谓词下推 --> JOIN与FILTER交换,将可以提前的FILTER提前。
- 传递闭包 --> 通过关系代数推算FILTER信息,提前过滤。
- Runtime Filter --> 用min-max,in-list bloom filter等减小搜索范围。
4.RBO的优缺点
- 优点:实现简单,优化速度快;
- 缺点:不保证得到最优的执行计划。
3.CBO
Cost-based Optimzer(CBO) :使用一个模型估算执行计划的代价,选择代价最小的执行计划。
1. 原始表统计信息 + 推导规则
-
原始表表统计信息
- 表或者分区级别:行数、行平均大小、表在磁盘中占用了多少字节等。
- 列级别:min、max、num nulls、num、not nulls、num、distinct value(NDV)、histogram 等。
-
推导统计信息
- 选择率(selectivity) --> 对于某一个过滤条件,查询会从表中返回多大比例的数据。
- 基数(cardinality) --> 基本含义是表的 unique 行数,在查询计划中常指算子需要处理的行数。
-
统计信息的收集方式
- 在DDL里指定需要收集的统计信息,数据库会在数据写入时收集或者更新统计信息;
- 手动执行 explain analyze statement,触发数据库收集或者更新统计信息;
- 动态采样。
2. 算子代价计算规则
算子代价:CPU、内存、磁盘I/O、网络I/O等。
-
和算子的统计信息有关,比如输入、输出结果的行数,每行大小等。
- 叶子算子 scan --> 通过统计原始表数据得;
- 中间算子 --> 根据一定的推导规则,从下层算子的统计信息推导得到。
-
和具体的算子类型,以及算子的物理实现有关。
3.计算执行计划代价
- 分而治之,执行计划的代价等于所有算子的执行代价之和。
- 通过 RBO 得到(所有)可能的等价执行计划(非原地替换)。
4.执行计划枚举
通常使用 贪心算法 或 动态规划 选出最优的执行计划。
四、查询优化器的社区开源实践
主流的查询优化器都包含RBO和CBO。
1.Apache Calcite
大数据领域很流行的查询优化器。
2.Calcite RBO
定义了许多优化规则,使用 pattern 匹配子树,执行等价变换。
3.Calcite CBO
基于Volcano/Cascade框架。(Volcano/Cascad的精髓:Memo、动态规划、剪枝)
五、SQL 相关的前沿趋势
- 存储计算分离、一体化(HSAP, HTAP, HTSAP)。
- Cloud Native、 Serverless。
- 湖仓一体(数据仓库,数据湖)、联邦查询。
- DATA + AI(AI4DB、DB4AI)
参考文章: SQL查询优化器浅析 周东炎ppt