mysql代价模型由mysql.server_cost和mysql.engine_cost两张可配置系统表驱动,优化器据此结合统计信息动态计算执行计划成本;修改表后需执行flush optimizer_costs生效。

MySQL 查询优化器是否选错索引,往往不是因为统计信息不准,而是系统变量悄悄改写了代价模型的“计分规则”。直接看 EXPLAIN 只能看到结果,不查变量就等于蒙眼调优。
为什么修改 row_evaluate_cost 会强制走索引?
这个变量控制“评估一行是否满足 WHERE 条件”的 CPU 成本,默认是 0.2。当它被人为调高(比如设为 1.0),全表扫描的 CPU 成本就会飙升:原先是 rows × 0.2,现在变成 rows × 1.0。优化器一算,哪怕索引需要多几次 IO,总成本也可能更低。
- 典型场景:大表上
WHERE status = 1这类低区分度条件,原本走全表扫描,调高后立刻转向索引 - 注意:该变量只影响单表扫描路径的成本计算,对 JOIN 或子查询中内表的行评估不生效
- 验证方式:
SELECT * FROM mysql.server_cost WHERE cost_name = 'row_evaluate_cost';
optimizer_switch 中哪些开关真正影响代价估算?
不是所有开关都参与成本计算。真正左右优化器“算账”的关键项有:
-
index_condition_pushdown=on:允许在存储引擎层提前过滤,降低回表行数 → 直接减少row_evaluate_cost实际作用的行数 -
mrr_cost_based=on:决定是否基于成本启用 Multi-Range Read;若关掉,即使mrr=on,优化器也不会考虑 MRR 路径 -
use_index_extensions=on:影响二级索引是否能“隐式包含主键列”,改变索引覆盖判断 → 改变是否需要回表,从而影响 IO 成本权重 -
skip_scan=on:开启松散索引扫描,对WHERE a = ? AND b > ?类查询提供新路径,其成本公式独立于普通 range 扫描
执行 SELECT @@optimizer_switch; 后,重点盯住这几个开关的状态,别被一堆 on 模糊了焦点。
查看和修改代价常量必须操作的两张表
MySQL 8.0+ 把代价参数拆成两类,存在两个系统表里,缺一不可:
-
mysql.server_cost:全局级操作成本,如row_evaluate_cost、key_compare_cost -
mysql.engine_cost:按存储引擎区分的 IO 成本,如io_block_read_cost(InnoDB 默认1.0) - 修改前必须先
FLUSH OPTIMIZER_COSTS;,否则新值不生效 - 示例:把 InnoDB 的磁盘块读成本从
1.0改为0.5(模拟 SSD 环境),命令是:UPDATE mysql.engine_cost SET cost_value = 0.5 WHERE engine_name = 'InnoDB' AND device_type = 0 AND cost_name = 'io_block_read_cost'; FLUSH OPTIMIZER_COSTS;
代价模型不是黑盒,但它的“可配置性”恰恰是最容易被忽略的复杂点——同一套 SQL,在不同实例上因 server_cost 表里一行数据不同,就可能生成完全不同的执行计划。上线前务必核对这两张表的内容,而不是只抄 my.cnf 配置。











