目录

概述

查询代价估算

代价模型

选择率计算的常用方法

单表扫描代价

常用的单表扫描算法

单表扫描代价计算

索引

如何利用索引

两表连接算法

基本的两表连接算法

嵌套循环连接算法

排序归并连接算法

Hash 连接算法

进一步认识两表连接算法

连接操作代价计算

Mysql代价模型的实现方式

代价模型的工作方式

代价模型数据库

结论


概述

基于代价的优化器引擎可能面临的问题和挑战如下。

  • 从可选的单表扫描方式中,挑选什么样的单表扫描方式是最优的?
  • 对于两个表连接时,如何连接是最优的?
  • 对于多个表连接,连接顺序有多种组合,哪种连接顺序是最优的?
  • 对于多个表连接,连接顺序有多种组合,是否要对每种组合都探索?如果不全部探索,怎么找到最优的一种组合?

那么碰到这些问题,我们如何解决呢,这里就引入了一个非常重要的功能叫做代价估算模型(Cost Model),我们用代价估算模型计算出各种情况下执行的代价,然后优化器选择一个代价最小的计划,来作为后续执行的计划。

查询代价估算

查询代价估算的重点是代价估算模型,这是物理查询优化的依据。除了代价模型外,选择率也是很重要的一个概念,对代价求解起着重要作用。

代价模型

查询代价估算基于 CPU 代价和 IO 代价,所以代价模型可以用以下计算公式表示:

总代价 = IO 代价 + CPU 代价COST = P*a_page_cpu_time + W * T

在上面的两个公式中:

  • P 为计划运行时访问的页面数,a_page_cpu_time 是每个页面读取的时间花费,其乘积反映了 IO 花费。
  • T 为访问的元组数,反映了 CPU 花费(存储层是以页面为单位,数据以页面的形式 被读入内存,每个页面上可能有多条元组,访问元组需要解析元组结构,才能把元组 上的字段读出,这消耗的是 CPU)。如果是索引扫描,则还会包括索引读取的花费。
  • W 为权重因子,表明 IO 到 CPU 的相关性,又称选择率(selectivity) 一。选择率用于 表示在关系 R 中,满足条件“ A <op> a ”的元组数与 R 的所有元组数 N 的比值。

选择率计算的常用方法

选择率在代价估算模型中占有重要地位,其精确程度直接影响最优计划的选取。选择率计算的常用方法如下:

  • 无参数方法(Non-Parametric Method)。使用 ad hoc 数据结构或直方图维护属性值的 分布,最常用的是直方图方法。
  • 参数法(Parametric Method)。使用具有一些自由统计参数(参数是预先估计出来的) 的数学分布函数逼近真实分布。
  • 曲线拟合法(Curve Fitting)。为克服参数法的不灵活性,用一般多项式和标准最小 方差来逼近属性值的分布。
  • 抽样法(sampling)。从数据库中抽取部分样本元组,针对这些样本进行查询,然后 收集统计数据,只有足够的样本被测试之后,才能达到预期的精度。
  • 综合法。将以上几种方法结合起来,如抽样法和直方图法结合。

单表扫描代价

单表扫描需要从表上获取元组,直接关联到物理IO的读取,所以不同的单表扫描方式,有不同的代价。

常用的单表扫描算法

单表扫描是完成表连接的基础。对于单表数据的获取,通常有如下方式:

  • 全表扫描表数据。为获取表的全部元组,读取表对应的全部数据页。
  • 局部扫描表数据。为获取表的部分元组,读取指定位置对应的数据页。

对于全表扫描,通常采取顺序读取的算法。为了提高表扫描的效率,有很多算法和优化方式被提出来。单表扫描和 IO 操作密切相关,所以很多算法在 IO 上倾注精力。

常用的单表扫描算法如下:

  • 顺序扫描(SeqScan)。从物理存储上按照存储顺序直接读取表的数据。当无索引可用,或访问表中的大部分数据,或表的数据量很小时,使用顺序扫描效果较好。
  • 索引扫描(IndexScan)。根据索引键读索引,找出物理元组的位置。根据从索引中找到的位置,从存储中读取数据页面。索引扫描可以将元组按排序的顺序返回。索引扫描时若选择率较低,则读数据花费的 IO 会显著减少。换句话说,如果选择率很高,不适宜使用索引扫描。
  • 只读索引扫描(IndexOnlyScan)。根据索引键读索引,索引中的数据能够满足条件判断,不需要读取数据页面。比索引扫描少了读取数据的 IO 花费。
  • 行扫描(RowIdScan)。用于直接定位表中的某一行。对于元组,通常为元组增加特殊的列,可以通过特殊的列计算出元组的物理位置,然后直接读取元组对应的页面, 获取元组。在 PostgreSQL 中称为 Tid 扫描,此种方式是在元组头上增加名为 CTID 的列,用这列的值可以直接计算本条元组的物理存储位置。
  • 并行表扫描(ParallelTableScan)。对同一个表,并行地、通过顺序的方式获取表的 数据,结果是得到一个完整的表数据。
  • 并行索引扫描(ParallelIndexScan)。对同一个表,并行地、通过索引的方式获取表 的数据,将结果合并在一起。
  • 组合多个索引扫描。有的系统称为 MultipleIndexScan。对同一个元组的组合条件 (AND 或者 OR 谓词组合的多个索引列上的多条件查询)进行多次索引扫描,然后在内存里组织一个位图,用位图描述索引扫描结果中符合索引条件的元组位置。组合多个索引(包括同一索引的多次使用)来处理单个索引扫描不能实现的情况。本质上不是单表的扫描方式,是构建在单表的多个索引扫描基础上的。

对于局部扫描,根据数据量的情况和元组获取条件,可能采用顺序读取或随机读取存储系统的方式。选择率在这种情况下会起一定作用。如果选择率的值很大,意味着采取顺序扫描方式可能比局部扫描的随机读的方式效率更高,这是因为顺序 IO 会减少磁盘头移动的等待时间,如果数据库文件在磁盘上没有碎片,这种扫描方式对性能的改善将更为明显。对于大表,顺序扫描会一次读取多个页,这将进一步降低顺序表扫描的开销。

对于局部扫描,通常采用索引,实现少量数据的读取优化。这是一种随机读取数据的方式。虽然顺序表扫描可能会比读取许多行的索引扫描花费的时间少,但如果顺序扫描被执行多次,且不能有效地利用缓存,则总体花费巨大。索引扫描访问的页面可能较少,而且这些页很可能会保存在数据缓冲区,访问的速度会更快。所以,对于重复的表访问(如嵌 套循环连接的右表),采用索引扫描比较好。究竟采取哪种扫描方式,查询优化器在采用代价估算比较代价的大小后才决定。

有的系统对于随机读采取了优化措施,即把要读取的数据的物理位置排序,然后一批 读入,保障了磁盘单向一次扫描即可获取一批数据,提高了 IO 效率。

在并行操作的时候,可能因不同隔离级别的要求,需要解决数据一致性的问题。如可串行化的处理,需要在表级加锁或者表的所有元组上加锁,这是因为索引扫描只在满足条 件的元组上加锁,所以索引扫描在多用户环境中可能会比顺序扫描效率高。查询优化器在此种情况下倾向于选择索引扫描,这是一条启发式优化规则。

单表扫描代价计算

因单表扫描需要把数据从存储系统中调入内存,所以单表扫描的代价需要考虑 IO 的花费。顺序扫描,主要是 IO 花费加上元组从页面中解析的花费。索引扫描和其他方式的扫描, 由于元组数不是全部元组,需要考虑选择率的问题。单表扫描操作的代价估算公式如下所示。

单表扫描算法代价表

扫描方式

代价估算公式

顺序扫描

N_page * a_tuple_IO_time + N_tuple * a_tuple_CPU_time

索引扫描

C_index + N_page_index * a_tuple_IO_time

上表参数说明如下:

  • a_page_IO_time,一个页面的 IO 花费。
  • N_page,数据页面数。
  • N_page_index,索引页面数。
  • a_tuple_CPU_time,一个元组从页面中解析的 CPU 花费。
  • N_tuple,元组数。
  • C_index,索引的 IO 花费,C_index = N_page_index×a_page_IO_time。
  • N_tuple_index,索引作用下的可用元组数,N_tuple_index =N_tuple× 索引选择率。

索引

索引是建立在表上的,本质上是通过索引直接定位表的物理元组,加快数据获取的方式,所以索引优化的手段应该归属到物理查询优化阶段。

如何利用索引

索引是提高查询效率的有效手段。如果某个列上存在索引,并不意味着索引能够有效 使用。通常查询优化器使用索引的原则如下:

  • 索引列作为条件出现在 WHERE、HAVING、ON 子句中,这样有利于利用索引过滤 元组。
  • 索引列是被连接的表(内表)对象的列且存在于连接条件中。

除了上述的两种情况外,还有一些特殊情况可以使用索引,如排序操作、在索引列上 求 MIN、MAX 值等。

示例如下。首先创建表:

night=# CREATE TABLE A (a1 INT UNIQUE, a2 VARCHAR(9), a3 INT);

注意: CREATE TABLE / UNIQUE 将要为表 "a" 创建隐含索引 "a_a1_key"

对表做查询,没有列对象作为过滤条件(如出现在 WHERE 子句中),只能顺序扫描, 例如:

night=# EXPLAIN SELECT  A.* FROM  A;QUERY PLAN------------------------------------------------------Seq Scan on a (cost=0.00..21.00 rows=1100 width=44)(1 行记录 )

对表做查询,有列对象且索引列作为过滤条件,可做索引扫描,例如:

night=# EXPLAIN SELECT  A.* FROM  A WHERE  A.a1 = 1;QUERY PLAN -------------------------------------------------------------------Index Scan using a_a1_key on a (cost=0.00..8.27 rows=1 width=44) Index Cond: (a1 = 1)(2 行记录 )

对表做查询,有列对象作为过滤条件,但索引列被运算符“ - ”处理,查询优化器不能

在执行前进行取反运算,不可利用索引扫描,只能做顺序扫描。例如:

night=# EXPLAIN SELECT  A.* FROM  A WHERE  - A.a1 =- 2; QUERY PLAN---------------------------------------------------Seq Scan on a (cost=0.00..26.50 rows=6 width=44)Filter: ((- a1) = (-2)) (2 行记录 )

对表做查询,有列对象作为过滤条件,且目标列没有超出索引列,可做只读索引扫描, 这种扫描方式比单纯的索引扫描的效率更高。例如:

night=# EXPLAIN SELECT  A.a1 FROM  A WHERE  A.a1 = 1; QUERY PLAN-----------------------------------------------------------------------Index Only Scan using a_a1_key on a (cost=0.00..3.16 rows=1 width=4)Index Cond: (a1 = 1) (2 行记录 )

对表做查询,有索引存在,但选择条件不包括索引列对象,只能使用顺序扫描。例如:

night=# EXPLAIN SELECT  A.a1 FROM  A WHERE  A.a3 = 1; QUERY PLAN--------------------------------------------------Seq Scan on a (cost=0.00..23.75 rows=6 width=4)Filter: (a3 = 1) (2 行记录 )

对表做查询,有索引存在,选择条件包括索引列对象,可使用索引扫描,对选择条件中不存在索引的列作为过滤器被使用。例如:

night=# EXPLAIN SELECT  A.a1 FROM  A WHERE  A.a1 = 1   AND A.a3 = 1;QUERY PLAN-----------------------------------------------------------------Index Scan using a_a1_key on a (cost=0.00..8.27 rows=1 width=4) Index Cond: (a1 = 1)Filter: (a3 = 1)/* 不存在索引的列作为过滤器被使用 */ (3 行记录 )

对表做查询,有索引存在,选择条件包括索引列对象,但索引列对象位于一个表达式中,参与了运算,不是“ key= 常量”格式,则索引不可使用,只能是顺序扫描。例如:

night=#EXPLAIN SELECT  A.* FROM  A WHERE  A.a1 + A.a3 = 2;// 索引列在表达式中 QUERY PLAN---------------------------------------------------Seq Scan on a (cost=0.00..26.50 rows=6 width=44)Filter: ((a1 + a3) = 2) (2 行记录 )night=# EXPLAIN SELECT  A.* FROM  A WHERE  A.a1 = 2- A.a3;// 索引列参与运算, 但不可使用索引扫描 QUERY PLAN---------------------------------------------------Seq Scan on a (cost=0.00..26.50 rows=6 width=44)Filter: (a1 = (2 - a3)) (2 行记录 )night=# EXPLAIN SELECT  A.a1 FROM  A WHERE  A.a1 < 1+2;// 索引列不在 1+2 的表达式中,被独立使用,可使用索引扫描QUERY PLAN------------------------------------------------------------------Bitmap Heap Scan on a (cost=7.09..21.68 rows=367 width=4)Recheck Cond: (a1 < 3)->Bitmap Index Scan on a_a1_key (cost=0.00..7.00 rows=367 width=0)Index Cond: (a1 < 3) (4 行记录 )

对表做查询,有索引列对象作为过滤条件,操作符是范围操作符 > 或 <,可做索引扫 描。例如:

night=# EXPLAIN SELECT  A.a1 FROM  A WHERE  A.a1 > 1;QUERY PLAN------------------------------------------------------------------Bitmap Heap Scan on a (cost=7.09..21.68 rows=367 width=4)Recheck Cond: (a1 > 1)->Bitmap Index Scan on a_a1_key (cost=0.00..7.00 rows=367 width=0) /* 使用了位图索引扫描 */Index Cond: (a1 > 1) (4 行记录 )night=# EXPLAIN SELECT  A.a1 FROM  A WHERE  A.a1 < 1;QUERY PLAN------------------------------------------------------------------Bitmap Heap Scan on a (cost=7.09..21.68 rows=367 width=4)Recheck Cond: (a1 < 1)->Bitmap Index Scan on a_a1_key (cost=0.00..7.00 rows=367 width=0) /* 使用了位图索引扫描 */Index Cond: (a1 < 1) (4 行记录 )

对表做查询,有索引列对象作为过滤条件,操作符是范围操作符 <>,不可做索引扫描。例如:

night=# EXPLAIN SELECT  A.a1 FROM  A WHERE  A.a1 <> 1; QUERY PLAN-----------------------------------------------------Seq Scan on a (cost=0.00..23.75 rows=1099 width=4)Filter: (a1 <> 1) (2 行记录 )

对表做查询,有索引列对象作为过滤条件,操作符是范围操作符 BETWEEN-AND,可 做索引扫描。例如:

night=# EXPLAIN SELECT  A.a1 FROM  A WHERE  A.a1 BETWEEN 1   AND 2; QUERY PLAN----------------------------------------------------------------------- Bitmap Heap Scan on a (cost=4.31..13.79 rows=6 width=4)Recheck Cond: ((a1 >= 1) AND (a1 <= 2))->Bitmap Index Scan on a_a1_key (cost=0.00..4.31 rows=6 width=0)Index Cond: ((a1 >= 1) AND (a1 <= 2)) (4 行记录 )

所以,对于索引列,索引可用的条件如下:

  • 在 WHERE、JOIN/ON、HAVING 的条件中出现“ key <op> 常量”格式的条件子句(索引列不能参与带有变量的表达式的运算)。
  • 操作符不能是 <> 操作符(不等于操作符在任何类型的列上不能使用索引,可以认为这是一个优化规则,在这种情况下,顺序扫描的效果通常好于索引扫描)。
  • 索引列的值选择率越低,索引越有效,通常认为选择率小于 0.1 则索引扫描效果会好一些。

两表连接算法

关系代数的一项重要操作是连接运算,多个表连接是建立在两表之间连接的基础上的。研究两表连接的方式,对连接效率的提高有着直接的影响。

基本的两表连接算法

基本的两表连接算法主要有嵌套循环连接算法、归并连接算法、Hash连接算法等。

嵌套循环连接算法

两表做连接,采用的最基本算法是嵌套循环连接算法。算法的描述如下:

FOR EACH ROW r1 IN t1 {     FOR EACH ROW r2 IN t2 {        IF r1,r2 SATISFIES JOIN CONDITIONS             JOIN r1,r2    } }

数据库引擎在实现该算法的时候,以元组为单位进行连接。元组是从一个内存页面获 取来的,而内存页面是从存储系统通过 IO 操作获得的,每个 IO 申请以“块”为单位尽量 读入多个页面。所以,如果考虑获取元组的方式,则可以改进嵌套循环连接算法,改进后 的算法称为基于块的嵌套循环连接算法。算法描述如下:

FOR EACH CHUNK c1 OF t1 {  IF c1 NOT IN MEMORY // 系统一次读入多个页面 , 所以不需要每次都从存储系统读入 , 消耗 IO    READ CHUNK c1 INTO MEMORY  FOR EACH ROW r1 IN CHUNK c1 {// 从页面中分析出元组 , 消耗 CPU    FOR EACH CHUNK c2 OF t2 {       IF c2 NOT IN MEMORY        READ CHUNK c2 INTO MEMORY      FOR EACH ROW r2 IN c2 {// 从页面中分析出元组 , 消耗 CPU        IF r1,r2 SATISFIES JOIN CONDITIONS           JOIN r1,r2      }    }  }}

无论是嵌套循环连接还是基于块的嵌套循环连接,其本质都是在一个两层的循环中拿 出各自的元组,逐一匹配是否满足连接条件。其他一些两表连接算法,多是在此基础上进 行的改进。如基于索引做改进,在考虑了聚簇和非聚簇索引 一的情况下,如果内表有索引可 用,则可以加快连接操作的速度。另外,如果内层循环的最后一个块使用后作为下次循环的第一个块,则可以节约一次 IO。如果外层元组较少,内层的元组驻留内存多一些(如有些查询优化器采用物化技术固化内层的元组),则能有效提高连接的效率。

嵌套循环连接算法和基于块的嵌套循环连接算法适用于内连接、左外连接、半连接、 反半连接等语义的处理。

排序归并连接算法

排序归并连接算法又称归并排序连接算法,简称归并连接算法。这种算法的步骤是: 为两个表创建可用内存缓冲区数为 M 的 M 个子表,将每个子表排好序;然后读入每个子表 的第一块到 M 个块中,找出其中最小的先进行两个表的元组的匹配,找出次小的匹配...... 依此类推,完成其他子表的两表连接。

归并连接算法要求内外表都是有序的,所以对于内外表都要排序。如果连接列是索引 列,可以利用索引进行排序。

归并连接算法适用于内连接、左外连接、右外连接、全外连接、半连接、反半连接等 语义的处理。

Hash 连接算法

基于 Hash 的两表连接算法有多种,常见的有如下 3 种:

  • 用连接列作为 Hash 的关键字,对内表进行 Hash 运算建立 Hash 表,然后对外表的每个元组的连接列用 Hash 函数求值,值映射到内表建立好的 Hash 表就可以连接了; 否则,探索外表的下一个元组。这样的 Hash 连接算法称为简单 Hash 连接(Simple Hash Join,SHJ)算法。
  • 如果把内表和外表划分成等大小的子表,然后对外表和内表的每个相同下标值的子表进行 SHJ 算法的操作,可以避免因内存小反复读入内外表的数据的问题。这样的 改进算法称为优美 Hash 连接(Grace Hash Join,GHJ)算法。
  • 结合了 SHJ 和 GHJ 算法的优点的混合 Hash 连接(Hybrid Hash Join,HHJ)算法。HHJ 算法是把第一个子表保存到内存不刷出,如果内存很大,则子表能容纳更大量的数据,效率接近于 SHJ。

Hash 类的算法都可能存在 Hash 冲突,如 GHJ 算法,当内存小或数据倾斜(不能均衡地分布到 Hash 桶,Hash 处理后集中在少量桶中)时,通过把一个表划分为多个子表的方式, 仍然不能消除反复读入的内外表数据的问题(称为“分区溢出”)。

Hash 连接算法只适用于数据类型相同的等值连接。Hash 连接需要存储 Hash 元组到 Hash 桶,要求较大的内存。如果表中连接列值重复率很高不能均匀分布,相同值的元组映射到少数几个桶中,Hash 连接算法效率就不会高。Hash 算法要求内表不能太大,通常查询优化器申请一段内存存放 Hash 表,如果超出且不能继续动态申请,则需要写临时文件,这会导致 IO 的颠簸。

Hash 连接算法适用于内连接、左外连接、右外连接、全外连接、半连接、反半连接等 语义的处理。

进一步认识两表连接算法

从内存的容量角度看,两表连接算法可以分为一趟算法、两趟算法,甚至多趟算法。所谓“趟”是指从存储系统获取全部数据的次数。一趟算法因内存空间能容纳下全部数据, 所以读取一次即可。两趟算法的第一趟从存储系统获取两表的数据,如做排序等处理后, 再写入外存的临时文件;第二趟重新读入临时文件进行进一步处理(有的算法对其中一个表 的元组只读取一次即可,属于一趟,因此两趟算法变为一趟半,但依然称为两趟算法)。多 趟算法的思想和两趟算法基本相同,用以处理更大量的数据。趟数是一种方式,不是算法 思想的改进,是代码实现中为减少 IO 所做的改进工作。

结合连接算法和索引、趟数的关系,两表连接算法对于查询优化器的意义如下所示。

算法

用索引改进算法(以 R 连接 S 为例)

趟数

嵌套连接

支持用索引改进算法对外表 R 的每一个元组,如果 r 的值可作为 S 连接列上的索引键值,用索引扫描 S 的元组,与 r 判断是否匹配

一趟

基于 Hash

不支持用索引改进算法 一趟读入 S 的数据,根据连接条件构造 Hash,把元组散列到桶中;对于 R,读入 一部分到缓冲区,对读入的每个元组散列,如果有同样散列值的桶已经在读入 S 的过 程中构造出来,则可进行连接;以此类推,逐步把 R 的其他部分读入缓存的同样方 式处理

一趟

基于排序

支持用索引改进算法 第一趟,利用索引进行内外表的排序;第二趟,读入两个表排序的数据进行连接

两趟

连接操作代价计算

连接操作花费 CPU 资源。从理论的角度分析 连接操作的代价估算原理如下所示。

两表连接算法的代价表

算法

代价估算公式

嵌套循环连接

基本的嵌套循环连接:C-outer + C-inner 内表使用索引改进嵌套循环连接:C-outer + C-inner-index

归并连接

基本的归并连接:C-outer + C-inner + C-outersort + C-innersort 内外表使用索引,只影响排序,C-outersort、C-innersort 可能变化

Hash 连接

C-createhash + (N-outer * N-inner * 选择率 ) * a_tuple_cpu_time

上表中涉及公式的参数如下:

  • a_tuple_cpu_time,获取一个元组消耗的 CPU 时间。
  • N-outer,扫描获取的外表元组数。
  • N-inner,扫描获取的内表元组数,N-inner = N-inner-all× 选择率,其中 N-inner-all
  • 表示内表的所有元组数。
  • C-outer,扫描外表的代价,C-outer = N-outer×a_tuple_cpu_time。
  • C-inner,扫描内表的代价,C-inner = N-inner ×a_tuple_cpu_time。
  • C-inner-index,使用索引扫描内表的代价,通常 C-inner-index 会小于 C-inner。
  • C-outersort,外表排序的代价。
  • C-innersort,内表排序的代价。
  • C-createhash,创建 Hash 的代价。

多表连接算法在后续文章中再进行介绍会涉及多表连接算法和对于雪花模型或者星型模型来讲,多表Join应该选择什么样的顺序执行? 多表代价估算等等。下面简述下Mysql是代价模型是如何实现的。

Mysql代价模型的实现方式

为什么需要配置cost model常量 ? 我们知道MySQL已经发展了好几十年的历史,但是在优化器中依然使用了hardcode的权重值来衡量io, cpu等资源情况,而这些权重值实际上是基于多年前甚至十来年前的经验设定的。想想看,这么多年硬件的发展多么迅速。几十上百个核心的服务器不在少数甚至在某些大型公司大规模使用,SSD早就成为主流,NVME也在崛起。高速RDMA网络正在走入寻常百姓家。这一切甚至影响到数据库系统的实现和变革。显而易见,那些hardcode的权值已经过时了,我们需要提供给用户可定义的方式,甚至更进一步的,能够智能的根据硬件环境自动设定。

MySQL为修改代价模型引入两个新的系统表, 通过这两个系统表暴露给用户来进行更新,如下:

SELECT * FROM MYSQL.SERVER_COST;

SELECT * FROM MYSQL.ENGINE_COST;

可以通过下面的命令修改

UPDATE MYSQL.SERVER_COST SET COST_VALUE = 100 WHERE  COST_NAME = 'DISK_TEMPTABLE_CREATE_COST';--使用最新的优化器参数FLUSH OPTIMIZER_COSTS; 

代价模型的工作方式

优化器代价模型按如下方式工作:

  • MySQL Server在启动时读取代价模型表(MYSQL.SERVER_COST、MYSQL.ENGINE_COST),并且在运行时使用内存中存贮的值。
  • MySQL Server会把表中任何非NULL的代价估算项的值都会覆盖在代码中写死的默认成本常数,优先参与优化器代价计算。表中的任何NULL值的代价估算项,优化器都会认为用户没有指定特定的值,而使用默认的代价常数。
  • 如果运行时需要变更代价估算项的值,可以通过Update命令修改后,通过FLUSH OPTIMIZER_COSTS命令重新加载最新的代价估算表。
  • 内存中缓存的代价项对当前正在执行的Session是不起效果的,一个Session内执行的Query其代价项的值是不会变动的。即使Server触发了重新读取代价表,任何估算项的变更也只影响后来链接上来的Session。
  • 代价开销表是不参与复制的,只影响修改的本地实例,不会通过复制把开销表的变更复制到备库。

代价模型数据库

优化器代价模型数据库由mysql系统数据库中的两个表组成,其中包含查询执行期间发生的操作的成本估算信息:

  • server_cost:用于一般服务器操作的优化器代价估算
  • engine_cost:针对特定存储引擎的特定操作的优化器代价估算

server_cost表包含以下列:

cost_name

代价模型中使用的代价估算的名称。 名称不区分大小写。 如果服务器在读取此表时无法识别成本名称,则会向错误日志中写入警告。

cost_value

代价估算值。 如果该值是非NULL,则服务器将其用作代价。 否则,它将使用默认估计值(编译值)。 DBA可以通过更新此列来更改代价估算。 如果服务器在读取此表时发现代价值无效(非正值),则会向错误日志中写入警告。要覆盖默认代价估算值(对于指定NULL的条目),请将代价设置为非NULL值。 要恢复为默认值,请将值设置为NULL。 然后执行FLUSH OPTIMIZER_COSTS告诉服务器重新读取代价表。

last_update

最后的更新时间。

comment

与代价估算相关的描述性注释。 DBA可以使用此列来提供有关代价估算行为何存储特定值的信息。

default_value

代价估算的默认(内置)值。 该列是只读生成的列,即使更改了相关的代价估算,它也会保留其值。 对于在运行时添加到表中的行,此列的值为NULL。

server_cost表的主键是cost_name列,因此无法为任何成本估算创建多个条目。

Mysql服务器识别server_cost表的这些cost_name值:

disk_temptable_create_cost, disk_temptable_row_cost

在基于磁盘的存储引擎(InnoDB或MyISAM)中存储的内部创建的临时表的代价估算。 增加这些值会增加使用内部临时表的代价估算,并使优化器更喜欢较少使用它们的查询计划。与相应内存参数的默认值(memory_temptable_create_cost,memory_temptable_row_cost)相比,这些磁盘参数的默认值较大,反映了处理基于磁盘的表的代价较高。

key_compare_cost

比较记录键的代价。 增加此值将导致查询计划比较多个键变得更加昂贵。 例如,与避免使用索引进行排序的查询计划相比,执行文件排序的查询计划变得相对昂贵。

memory_temptable_create_cost, memory_temptable_row_cost

内部存储在MEMORY存储引擎中的临时表的代价估算。 增加这些值会增加使用内部临时表的代价估算,并使优化器更喜欢较少使用它们的查询计划。 与相应磁盘参数的默认值(disk_temptable_create_cost,disk_temptable_row_cost)相比,这些内存参数的默认值较小,反映了处理基于内存的表的代价较低。

row_evaluate_cost

评估记录条件的代价。 与检查行数较少的查询计划相比,增加该值会导致检查许多行的查询计划变得更加昂贵。 例如,与读取较少行的范围扫描相比,表扫描变得相对昂贵。

engine_cost表包含以下列:

engine_name

此代价估算适用的存储引擎的名称。 名称不区分大小写。 如果该值为默认值,则适用于所有没有自己命名条目的存储引擎。 如果服务器在读取该表时无法识别引擎名称,则会向错误日志中写入警告。

device_type

此费用估算适用的设备类型。 该列旨在为不同的存储设备类型(例如,硬盘驱动器与固态驱动器)指定不同的代价估算。 当前,此信息未使用,并且0是唯一允许的值。

cost_name

代价模型中使用的代价估算的名称。 名称不区分大小写。 如果服务器在读取此表时无法识别成本名称,则会向错误日志中写入警告。

cost_value

代价估算值。 如果该值是非NULL,则服务器将其用作代价。 否则,它将使用默认估计值(编译值)。 DBA可以通过更新此列来更改代价估算。 如果服务器在读取此表时发现代价值无效(非正值),则会向错误日志中写入警告。要覆盖默认代价估算值(对于指定NULL的条目),请将代价设置为非NULL值。 要恢复为默认值,请将值设置为NULL。 然后执行FLUSH OPTIMIZER_COSTS告诉服务器重新读取代价表。

last_update

最后的更新时间。

comment

与代价估算相关的描述性注释。 DBA可以使用此列来提供有关代价估算行为何存储特定值的信息。

default_value

代价估算的默认(内置)值。 该列是只读生成的列,即使更改了相关的代价估算,它也会保留其值。 对于在运行时添加到表中的行,此列的值为NULL,不同之处在于,如果该行具有与原始行之一相同的cost_name值,则default_value列与该行具有相同的值。

engine_cost表的主键是一个由(cost_name,engine_name,device_type)列组成的元组,因此无法为这些列中的任何值组合创建多个条目。

Mysql服务器识别engine_cost表的这些cost_name值:

io_block_read_cost

从磁盘读取索引或数据块的代价。 与读取较少磁盘块的查询计划相比,增加该值会使读取许多磁盘块的查询计划变得更加昂贵。 例如,与读取较少块的范围扫描相比,表扫描变得相对昂贵。

memory_block_read_cost

与io_block_read_cost相似,但表示从内存数据库缓冲区读取索引或数据块的开销。

如果io_block_read_cost和memory_block_read_cost值不同,则执行计划可能在同一查询的两次运行之间改变。 假设内存访问的代价小于磁盘访问的代价。 在这种情况下,在服务器启动之前,将数据读入缓冲池之前,您可能会得到与运行查询之后不同的计划,因为这样数据就已经在内存中了。

结论

本文介绍了代价模型相关的内容,代价模型在基于代价的优化器中起着非常重要的作用,是核心中的核心,代价模型的好坏决定着基于代价的优化器的引擎的好坏。

参考资料

  • https://dev.mysql.com/doc/refman/8.0/en/cost-model.html
  • http://hbasefly.com/2017/05/04/bigdata-cbo/
  • 《数据库查询优化器的艺术:原理解析与SQL性能优化》
  • http://mysql.taobao.org/monthly/2016/10/09/
  • http://mysql.taobao.org/monthly/2020/03/08/
  • http://mysql.taobao.org/monthly/2018/10/02/
  • http://mysql.taobao.org/monthly/2016/05/09/
  • http://mysql.taobao.org/monthly/2016/07/02/
  • http://mysql.taobao.org/monthly/2017/02/07/
  • http://mysql.taobao.org/monthly/2017/10/02/
  • http://mysql.taobao.org/monthly/2017/01/10/
  • http://mysql.taobao.org/monthly/2019/01/04/

分享大数据行业的一些前沿技术和手撕一些开源库的源代码
微信公众号名称:技术茶馆
微信公众号ID    :    Night_ZW

【数据库内核】基于代价的优化器引擎-代价估算相关推荐

  1. MaxCompute理解数据、运算和用户的大脑:基于代价的优化器

    摘要: 回顾大数据技术领域大事件,最早可追溯到06年Hadoop的正式启动,而环顾四下,围绕着数据库及数据处理引擎,业内充斥着各种各样的大数据技术.在云栖社区2017在线技术峰会大数据技术峰会上,阿里 ...

  2. oracle sql 查询优化器,基于ORACLE成本优化器的SQL查询优化分析与应用

    第 39 卷 第 2 期2018 年 3 月 内蒙古农业大学学报( 自 然 科 学 版 ) Journal of Inner Mongolia Agricultural University ( Na ...

  3. 【Oracle】看懂执行计划之基于成本的优化器(CBO)

    [Oracle]看懂执行计划之基于成本的优化器(CBO) 基于代价的优化方式   Cost-Based Optimization,简称 CBO.CBO 选择目标 SQL 执行计划的判断原则是成本,从目 ...

  4. Apache Spark 2.2中基于成本的优化器(CBO)(转载)

    Apache Spark 2.2最近引入了高级的基于成本的优化器框架用于收集并均衡不同的列数据的统计工作 (例如., 基(cardinality).唯一值的数量.空值.最大最小值.平均/最大长度,等等 ...

  5. oracle optimizer_features_enable,Oracle Optimizer:迁移到使用基于成本的优化器—–系列2.1-数据库专栏,ORACLE...

    oracle optimizer:迁移到使用基于成本的优化器-–系列2.1 系列之二包含影响优化器选择执行计划的初始化参数和oracle内部隐藏参数,合理设置这些参数对于优化器是相当重要的. 6.影响 ...

  6. oracle成本cbo,基于成本的优化器(CBO)

    本短文着意于消除一些关于基于成本的优化器(CBO)的错误说法,强调一般的错误和问题. Background 背景 ~~~~~~~~~~ 为了执行任何一个SQL语句,Oracle都要先导出一个" ...

  7. 95-874-040-源码-批处理-Flink批处理优化器值成本估算

    文章目录 1.视图 3.成本估算 4. 什么是成本 5.分类 5.1 网络成本: 6.如何估算成本 预算提供者 1.视图 3.成本估算 ​ 在基于成本的优化器中,成本估算非常重要,它直接影响着候选计划 ...

  8. 【优化求解】基于水母搜索优化器JS算法求解最优目标matlab源码

    1 简介 1.1  人工水母算法原理背景 水母生活在世界上不同深度和温度的水中.它们酷似钟状,一些水母的直径小于1cm,然有些水母直径则非常大.它们有各种各样的颜色.大小和形状.大多数水母偏好海洋环境 ...

  9. 【优化求解】基于水母搜索优化器JS算法求解多目标优化问题matlab源码

    1 简介 1.1  人工水母算法原理背景 水母生活在世界上不同深度和温度的水中.它们酷似钟状,一些水母的直径小于1cm,然有些水母直径则非常大.它们有各种各样的颜色.大小和形状.大多数水母偏好海洋环境 ...

  10. 【智能优化算法-黑猩猩算法】基于增强型黑猩猩优化器算法求解单目标优化问题附matlab代码

    1 内容介绍 This article proposes a novel metaheuristic algorithm called Chimp Optimization Algorithm (Ch ...

最新文章

  1. R语言dplyr包filter函数通过逻辑条件过滤数据实战
  2. DB2 runstats、reorgchk、reorg 命令【转载】
  3. MPSOC之3——centos环境配置及petalinux安装及使用
  4. 算法(3)--leetcode-explore-learn-数据结构-数组1
  5. Objective-C消息转发
  6. C语言 fclose 函数 - C语言零基础入门教程
  7. Linux查看负载 uptime,w ,top,iostat 命令
  8. 电商Sass平台-商城运营后台原型-仓储管理-订单管理-店铺运营-采购管理-数据分析-交易分析-留存分析-客户管理-用户运营-围栏管理-商品管理-流量分析-电商erp后台管理-用户权限-销量分析
  9. svm支持向量机分类方法
  10. Android: 页面元素抓取工具Weditor
  11. 非同步DCDC的工作模式(CCM、DCM和BCM)
  12. 学习嵌入式,需要哪些硬核技能?
  13. B. Silly Mistake
  14. 《三桃演义》第二回:返航,火星人柯里昂
  15. 如何利用SFTP在远程服务器中保障文件传输安全
  16. Chorme 浏览器查看请求头、响应头
  17. 最新双十一淘宝查历史最低价流量主小程序源码/亲测
  18. 海康硬盘录像机报警输出配置设置
  19. 前端 JS 调起打印机打印页面
  20. cnBeta 08年度精彩评论

热门文章

  1. 前端网页打印window.print()
  2. vue修饰符——.lazy
  3. Git:rebase 是什么
  4. 如何将电脑设置为定时关机?
  5. 问题描述-缺少关键字KeyError Traceback (most recent call last) <ipython-input
  6. 20172302 《Java软件结构与数据结构》第五周学习总结
  7. Dota2 on Ubuntu
  8. oracle peoplesoft enterprise,Solix实现与Oracle PeopleSoft Enterprise9.1整合
  9. 数据结构作业3-4(周)问题F:Turn off the light(关下灯)
  10. 技术问答-18 设计模式