为了生成执行计划,优化器会使用一个基于估算查询执行过程中各种操作开销的成本模型。MySQL优化器有一组在编译时默认指定的“成本常量”(例如CPU算几个因子、IO算几个因子),用于决策执行计划的生成。
从5.7.5开始,优化器在执行计划的生成过程中有了额外的成本估算项可用。这些估算项存在在mysql系统库的server_cost和engine_cost表中,并且任何时候都可以通过修改表中的值来配置这些估算项。这些表存在的目的是,可以通过简单的调整这些表中的成本估算项来影响执行计划的生成,来达到调整执行计划的目的。
两张表的结构和内容如下:
成本模型的工作方式
可配置的优化器成本模型按如下方式工作:
MySQL Server在启动时读取成本模型表,并且在运行时使用内存中存贮的值。表中任何非NULL的成本估算项的值都会覆盖在代码中写死的默认成本常数,优先参与优化器成本计算。任何NULL值的成本估算项优化器都会认为用户没有指定特定的值,而使用代码中默认的成本常数。
在MySQL运行时,Server可能会重新读取成本表,可以通过动态载入存储引擎或者执行FLUSH OPTIMIZER_COSTS语句来触发。
成本估算表可以让管理员通过简单的方式去调整成本估算项,也可以通过把估算项设置为NULL来恢复原来的内置默认值。优化器使用的是内存中缓存的开销值,所以修改了表中的值后记得用FLUSH OPTIMIZER_COSTS命令让修改生效。
内存中缓存的成本项对当前正在执行的Session是不起效果的,一个Session内执行的Query其成本项的值是不会变动的。即使Server触发了重新读取成本表,任何估算项的变更也只影响后来链接上来的Session。
成本开销表是不参与复制的,只影响修改的本地实例,不会通过复制把开销表的变更复制到备库。
成本模型数据库
优化器成本模型库由mysql系统库下的两张表组成,包含了Query执行过程中一些操作项的成本估算值:
server_cost: Server层一些操作的成本估算项的值
engine_cost: 特定引擎的一些操作的成本估算项的值
server_cost表包含这些字段:
cost_name
成本模型中的成本估算项的名称(不区分大小写)。如果Server无法识别名称,在读取的时候会打一个报错在error log中。
cost_value
成本估算项的值。如果值是非NULL的,那么Server就使用这个值作为成本,否则就用编译时内置的值,DBA可以通过UPDATE这个列来修改响应的成本项。如果Server读到无效的值(例如负数),会在errorlog中打一条Warning。
要覆盖内置的默认值就需要设置一个非NULL值,如果要恢复默认值,就把值重新改为NULL,然后执行FLUSH OPTIMIZER_COSTS 告诉Server重新读取成本表。
last_update
这一行的最后修改时间。
comment
成本项的描述注释。DBA可以利用这个这个列来记录为什么修改了这个成本项的值,用于备查。
server_cost表的主键是server_cost,所以不能创建名称相同的成本项。
Server可以识别server_cost表中如下的cost_name:
disk_temptable_create_cost, disk_temptable_row_cost
内部创建磁盘临时表的成本开销。增加这些成本项的值可以让优化器更偏向于生成不使用磁盘临时表的执行计划。
key_compare_cost
比较记录键值的成本开销。增加这个值可以让执行计划中比较键值的操作成本变的更加昂贵。例如,一个执行计划执行了filesort,那么它的代价会比利用索引避免排序的代价要大得多。
memory_temptable_create_cost, memory_temptable_row_cost
内部创建内存临时表的成本开销。增加这些值可以使得建立内部临时表成本增加,因而优化器会偏向于不使用临时表。
row_evaluate_cost
扫描记录行的成本开销。增加这个会导致执行计划中扫描很多行数据的操作变得更加昂贵,因而执行计划会偏向扫描更少的函数。例如,一个全表扫描会比范围扫描要昂贵的多。
engine_cost 表包含这些列:
engine_name
要应用这个成本项的存储引擎的名称(不区分大小写)。如果这些值是default,那么对所有没在表里指定的存储引擎都会生效。如果Server无法认出引擎名称,会在errorlog输出一条Warning。
device_type
这个成本项适用的设备类型。这个列可以为不同的存储设备指定不同的成本开销,例如SAS盘和SSD盘是不一样的。不过目前,这个信息还没启用,只有0可以设置。
cost_value,last_update,comment
这三列的含义跟server_cost表中的字段含义一样.
engine_cost表的主键是 (cost_name, engine_name, device_type),所以不允许为一个引擎的同一类存储设备创建相同的成本项。
目前Server只识别engine_cost表中的一个cost_name:
io_block_read_cost
这个成本项表示从磁盘读取一个数据的成本。增加这个值会导致执行计划中读取磁盘块会有更高的成本,因此优化器会偏向于读取更少的磁盘块。例如,一个全表扫描会比一个范围扫描读取更少的磁盘块,因此优化器会偏向范围扫描。