10.9.5 优化器成本模型
生成执行计划时,优化器使用基于查询执行中各种操作估算成本的成本模型。优化器拥有编译好的默认“成本常量”来决定执行计划。
优化器还有一個数据库,用于在执行计划构建时使用的估算成本。这些估算值存储在 server_cost
和 engine_cost
表中,位于 mysql
系统数据库中,并且可以随时配置。这些表的目的是使优化器使用的估算成本能够轻松地调整,以便于生成查询执行计划。
可配置的优化器成本模型工作如下:
-
服务器在启动时读取成本模型表到内存,然后在运行时使用内存中的值。任何非
NULL
成本估算指定在表中,优先于相应的编译好的默认成本常量。任何NULL
估算指示优化器使用编译好的默认值。 -
在运行时,服务器可能重新读取成本表。这种情况发生于存储引擎动态加载或执行
FLUSH OPTIMIZER_COSTS
语句。 -
成本表使服务器管理员轻松地调整成本估算,只需更改表中的条目。还可以通过将条目的成本设置为
NULL
来恢复默认值。优化器使用内存中的成本值,所以对表的更改需要执行FLUSH OPTIMIZER_COSTS
以生效。 -
会话开始时当前的内存成本估算将在整个会话中保持,直到会话结束。在特定情况下,如果服务器重新读取成本表,任何更改的估算只对后续启动的会话生效,而不是影响现有会话。
-
成本表是给定的服务器实例专用。服务器不复制成本表的变化到副本。
优化器成本模型数据库位于mysql
系统数据库中,包含查询执行过程中的成本估算信息:
server_cost
表包含这些列:
-
cost_name
成本估算名称。名称不区分大小写。如果服务器在读取该表时不能识别成本名,它将写入错误日志。
-
cost_value
成本估算值。如果值不是
NULL
,服务器就使用它作为成本。否则,它将使用默认估算(编译时的值)。DBA可以通过更新该列来更改成本估算。如果服务器在读取该表时发现成本值无效(非正数),它将写入错误日志。要覆盖默认成本估算(指定
NULL
的条目),设置成本为非NULL
值。要恢复到默认,设置值为NULL
。然后执行FLUSH OPTIMIZER_COSTS
以通知服务器重新读取成本表。 -
last_update
最后一次行更新时间。
-
comment
与成本估算相关的描述性注释。DBA可以使用该列来提供关于某个成本估算值存储的信息。
-
default_value
成本估算的默认(编译时)值。这一列是只读生成列,即使关联成本估算被更改,该值也保持不变。对于在运行时添加到表中的行,这一列的值为
NULL
。
服务器成本表的主键是cost_name
列,因此不能创建任何成本估算的多个条目。
服务器识别这些cost_name
值,为server_cost
表:
-
disk_temptable_create_cost
,disk_temptable_row_cost
对内存中创建的临时表(即使是
InnoDB
或MyISAM
)的成本估算。增加这些值将使优化器更偏好使用少量内存临时表的查询计划。关于这种表的信息,请参见第10.4.4节,“MySQL中内部临时表用法”。这些磁盘参数的默认值较大,相比对应的内存参数(
memory_temptable_create_cost
,memory_temptable_row_cost
)反映了处理磁盘表的更高成本。 -
key_compare_cost
比较记录键的成本。增加这个值将使比较多个键的查询计划变得更加昂贵。例如,避免排序使用索引的查询计划相比于执行
filesort
的查询计划变得更为昂贵。 -
memory_temptable_create_cost
,memory_temptable_row_cost
内存中创建的临时表在
MEMORY
存储引擎中的成本估算。增加这些值使优化器更偏好使用少量内存临时表的查询计划。关于这种表的信息,请参见第10.4.4节,“MySQL 中的内部临时表使用”。这些内存参数的默认值较小,相比对应磁盘参数 (
disk_temptable_create_cost
,disk_temptable_row_cost
) 的默认值反映了内存表的处理成本更低。 -
row_evaluate_cost
评估记录条件的成本。增加这个值使查询计划检查很多行变得相对较贵,相比检查少量行的查询计划。例如,表扫描相比读取少量行的范围扫描变得更贵。
engine_cost
表包含这些列:
-
engine_name
该成本估算适用的存储引擎名称。名称不区分大小写。如果值为
default
, 则适用于所有没有命名的存储引擎。如果服务器在读取这个表时不能识别引擎名称,它将写入错误日志。 -
device_type
该成本估算适用的设备类型。该列用于指定不同存储设备类型的成本估算,例如硬盘驱动器 versus 固态驱动器。当前,这个信息不被使用,0 是唯一允许的值。
-
cost_name
与
server_cost
表相同。 -
cost_value
与
server_cost
表相同。 -
last_update
与
server_cost
表相同。 -
comment
与
server_cost
表相同。 -
default_value
成本估算的默认值。这一列是只读生成列,即使关联的成本估算被更改,也会保留其值。对于在运行时添加到表中的行,除非该行的
cost_name
值与原来的行相同,该列的值为NULL
。
engine_cost
表的主键是由cost_name
、engine_name
和device_type
组成的元组,因此不能为这些列值组合创建多个条目。
服务器识别以下cost_name
值:
-
io_block_read_cost
从磁盘读取索引或数据块的成本。增加该值将使得读多个磁盘块的查询计划相比于读少个磁盘块的查询计划变得更贵。例如,表扫描相比于读少个块的范围扫描变得更贵。
-
memory_block_read_cost
与
io_block_read_cost
类似,但表示从内存数据库缓冲区中读取索引或数据块的成本。
如果io_block_read_cost
和memory_block_read_cost
值不同,执行计划可能在运行同一个查询语句两次时发生变化。假设内存访问成本小于磁盘访问成本。在这种情况下,在服务器启动前数据还没有被读入缓冲池,你可能会得到不同的计划,而后查询已经运行过了,因为那时数据就在内存中。
对于想从默认值更改成本模型参数的DBA,尝试将其值加倍或减半,并测量效果。
对io_block_read_cost
和memory_block_read_cost
参数的变化最可能产生有价值的结果。这些参数值使得数据访问方法的成本模型考虑来自不同来源的信息读取成本;例如,磁盘 versus 内存缓冲区中已经存在的信息读取成本。例如,设置io_block_read_cost
大于memory_block_read_cost
,优化器将偏好读取内存中已经存在的信息,而不是从磁盘读取。
以下示例展示如何更改默认值为io_block_read_cost
:
UPDATE mysql.engine_cost
SET cost_value = 2.0
WHERE cost_name = 'io_block_read_cost';
FLUSH OPTIMIZER_COSTS;
以下示例展示如何只对InnoDB
存储引擎更改io_block_read_cost
的值:
INSERT INTO mysql.engine_cost
VALUES ('InnoDB', 0, 'io_block_read_cost', 3.0,
CURRENT_TIMESTAMP, 'Using a slower disk for InnoDB');
FLUSH OPTIMIZER_COSTS;