目录
openGauss 数据库 SQL 引擎
一.SQL 引擎概览
二.SQL 解析
三. 查问优化
Ⅰ. 查问重写
Ⅱ. 门路搜寻
Ⅲ. 代价估算
openGauss 数据库执行器技术
openGauss 存储技术
openGauss 事务机制
openGauss 数据库安全
openGauss 数据库 SQL 引擎
三、查问优化
Ⅲ、代价估算
优化器会依据生成的逻辑执行打算枚举出候选的执行门路,要确保执行的高效,须要在这些门路中抉择开销最小、执行效率最高的门路。那么如何评估这些打算门路的执行开销就变得十分要害。代价估算就是来实现这项工作的,基于收集的数据统计信息,对不同的打算门路建设代价估算模型,评估给出代价,为门路搜寻提供输出。
- 统计信息
统计信息是计算打算门路代价的基石,统计信息的准确度对代价估算模型中行数估算和代价估算起着至关重要的作用,间接影响查问打算的优劣。openGauss 反对应用 Analyze 命令语句来实现对全库、单表、列、相关性多列进行收集统计信息。
因为统计信息间接影响代价计算的准确度,所以统计信息的收集的频率就是一个十分敏感的参数,如果统计信息收集的频率太低,则会导致统计信息的滞后,相同,如果过于频繁的收集统计信息,则会间接影响查问的性能。
通常数据库管理系统会提供手动的收集统计信息的办法,openGauss 反对通过 Analyze 命令来收集统计信息,同时数据库管理系统也会依据数据变动的状况主动决定是否从新收集统计信息,例如当一个表中的数据频繁的更新超过了一个阈值,那么就须要自动更新这个表的统计信息。在查问优化的过程中,如果优化器发现统计信息的数据曾经重大滞后,也能够发动统计信息的收集工作。
表级的统计信息通常包含元组的数量(N)、表占有的页面数 (B),而列级的统计信息则次要包含属性的宽度(W)、属性的最大值(Max)、最小值(Min)、高频值(MCV) 等等,通常针对每个列会建设一个直方图(H),将列中的数据依照范畴以直方图的形式展现进去,能够更不便的计算选择率。
直方图通常包含等高直方图、等频直方图和多维直方图等等,这些直方图能够从不同的角度来展示数据的散布状况,openGauss 采纳的是等高直方图,直方图的每个柱状体都代表了雷同的频率。
- 选择率
通过统计信息,代价估算零碎就能够理解一个表有多少行数据、用了多少个数据页面、某个值呈现的频率等,而后依据这些信息就能计算出一个约束条件(例如 SQL 语句中的 WHERE 条件)可能过滤掉多少数据,这种约束条件过滤出的数据占总数据量的比例称为选择率。
约束条件能够是独立的表达式形成的,也能够是由多个表达式形成的合取范式或析取范式,其中独立的表达式须要依据统计信息计算选择率,合取范式和析取范式则借助概率计算的办法取得选择率。
合取范式:P(A and B) = P(A) + P(B) – P(AB)
析取范式:P(AB) = P(A) × P(B)
假如要对约束条件 A > 5 AND B < 3 计算选择率,那么首先须要对 A > 5 和 B < 3 别离计算选择率,因为曾经有了 A 列和 B 列的统计信息,因而能够依据统计信息计算出 A 列中值大于 5 的数据比例,相似的还能够计算出 B 列的选择率。假如 A >5 的选择率为 0.3,B<3 的选择率为 0.5,那么 A > 5 AND B < 3 的选择率为:
P(A>5 and B<3)
= P(A>5) + P(B<3) – P(A>5)×P(B<3)
= 0.3 + 0.5 – 0.3×0.5
= 0.65
因为约束条件的多样性,选择率的计算通常会遇到一些艰难,例如选择率在计算的过程中通常假如多个表达式之间是互相“独立”的,但理论状况中不同的列之间可能存在函数依赖关系,因而这时候就可能导致选择率计算不精确。
- 代价估算办法
openGauss 的优化器是基于代价的优化器,对每条 SQL 语句,openGauss 都会生成多个候选的打算,并且给每个打算计算一个执行代价,而后抉择代价最小的打算。
当一个约束条件确定了选择率之后,就能够确定每个打算门路所须要解决的行数,并依据行数能够推算出所须要解决的页面数。当打算门路解决页面的时候,会产生 IO 代价,而当打算门路解决元组的时候(例如针对元组做表达式计算),会产生 CPU 代价,因为 openGauss 是分布式数据库,在 CN 和 DN 之间传输数据(元组)会产生通信的代价,因而一个打算的总体代价能够示意为:
总代价 = IO 代价 + CPU 代价 + 通信代价
openGauss 把所有程序扫描一个页面的代价定义为单位 1,所有其它算子的代价都归一化到这个单位 1 上。比方把随机扫描一个页面的代价定义为 4,即认为随机扫描一个页面所需代价是程序扫描一个页面所需代价的 4 倍。又比方,把 CPU 解决一条元组的代价为 0.01,即认为 CPU 解决一条元组所需代价为程序扫描一个页面所需代价的百分之一。
从另一个角度来看,openGauss 将代价又分成了启动代价和执行代价,其中:
总代价 = 启动代价 + 执行代价
1)启动代价
从 SQL 语句开始执行,到此算子输入第一条元组为止,所须要的代价,称为启动代价。有的算子启动代价很小,比方基表上的扫描算子,一旦开始读取数据页,就能够输入元组,因而启动代价为 0。有的算子的启动代价绝对较大,比方排序算子,它须要把所有上层算子的输入全副读取到,并且把这些元组排序之后,能力输入第一条元组,因而它的启动代价比拟大。
2)执行代价
从输入第一条元组开始,至查问完结,所须要的代价,称为执行代价。这个代价中又能够蕴含 CPU 代价、IO 代价和通信代价,执行代价的大小与算子须要解决的数据量无关,与每个算子实现的性能无关。解决的数据量越大、算子须要实现的工作越重,则执行代价越大。
3)总代价
代价计算是一个自底向上的过程,首先计算扫描算子的代价,而后依据扫描算子的代价计算连贯算子的代价以及 Non-SPJ 算子的代价。
图 8 代价计算示例
如图 8 所示,SQL 查问中蕴含两张表,别离为 t1、t2,它的某个候选打算的计算过程如下:
(1)扫描 t1 的启动代价为 0.00,总代价为 13.13。总代价中既包含了扫描表页面的 IO 代价,也包含了对元组进行解决的 CPU 代价,同理能够取得对 t2 表扫描的代价。
(2)因为连贯条件(t1.c1 = t2.c2)中的列与两表的散布列不同,因而该打算对 t2 进行了播送(Broadcast),播送算子的总代价为 15.18,此代价曾经包含了程序扫描 t2 的代价 13.13。
(3)应用 Hash Join 时,必须先为内表的数据建设 Hash 表,因而 Hash Join 具备启动代价,它的启动代价是 13.29,Hash Join 的总代价为 28.64。
(4)汇集算子的启动代价为 28.69,总代价为 28.79。
(5)顺次类推,此打算最终的启动代价为 29.31,总代价为 29.72。
至此,openGauss 数据库 SQL 引擎章节全副完结,下期文章将开始连载 openGauss 数据库执行器技术章节,敬请期待。
未完待续 ……