为了生成执行计划,优化器使用基于各种操作成本估算的成本模型。优化器具有编译好的默认 “成本常数”,以便在查询执行期间做出决策。
优化器还具有一个成本估算数据库,用于执行计划构建。这些估算存储在 mysql 系统数据库中的 server_cost 和 engine_cost 表中,并且可以随时配置。这些表的目的是使得可以轻松地调整优化器在生成查询执行计划时使用的成本估算。
可配置的优化器成本模型工作方式如下:
-
服务器在启动时将成本模型表读取到内存中,并在运行时使用内存中的值。任何非
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。
server_cost 表的主键是 cost_name 列,因此无法创建任何成本估算的多个条目。
服务器识别 server_cost 表中的以下 cost_name 值:
-
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记录键的比较成本。增加这个值将使比较许多键的查询计划变得更加昂贵。例如,执行文件排序的查询计划变得相对较昂贵,相比使用索引避免排序的查询计划。
-
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该成本估算适用的设备类型。该列旨在指定不同存储设备类型的成本估算,例如硬盘驱动器与固态驱动器。目前,该信息不被使用,0 是唯一允许的值。
-
成本名称与
server_cost表相同。 -
成本值与
server_cost表相同。 -
最后更新与
server_cost表相同。 -
注释与
server_cost表相同。 -
默认值成本估算的默认(编译时)值。该列是一个只读生成列,即使关联的成本估算更改,该值也保持不变。对于在运行时添加到表中的行,该列的值为
NULL,除非该行的成本名称值与原始行之一相同,则默认值列具有相同的值。
表 engine_cost 的主键是一个由 (成本名称, 引擎名称, 设备类型) 列组成的元组,因此无法为这些列的任何组合值创建多个条目。
服务器识别 engine_cost 表中的以下 成本名称 值:
-
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 参数的更改最可能产生有价值的结果。这些参数值使数据访问方法的成本模型考虑从不同来源读取信息的成本;即,从磁盘读取信息与从内存缓冲区读取信息的成本。例如,其他条件相同,设置 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;