MySQL优化器的query_cost是可验证、可拆解的I/O与CPU成本之和,但其准确性依赖统计信息质量、参数配置及数据分布,并非固定公式计算结果。

MySQL 优化器给出的 query_cost 不是拍脑袋估算的,而是可验证、可拆解的数值。它由 I/O 成本 + CPU 成本构成,但直接看 EXPLAIN FORMAT=JSON 的总值容易误判——因为成本估算严重依赖统计信息质量、参数配置和实际数据分布,不是固定公式套用就能对上。
怎么看真实执行成本:用 EXPLAIN FORMAT=JSON 提取 query_cost
这是最直接的入口,但要注意它只反映优化器“当前认为”的成本,不是实测耗时:
-
EXPLAIN FORMAT=JSON SELECT * FROM t1 WHERE a > 100返回的 JSON 中,顶层字段query_cost就是该计划总成本(单位:随机页读取) - 这个值来自
const_info子段,里面会分项列出io_cost和cpu_cost,比如"io_cost": 24.56, "cpu_cost": 12.89 - 别拿这个值去和慢日志里的
Query_time对比——一个是估算模型,一个是真实耗时,两者无线性关系
为什么算出来和 query_cost 对不上:统计信息不准或成本常量被改过
手动按默认常量(如 row_evaluate_cost=0.1、io_block_read_cost=1.0)套公式算,大概率不一致,常见原因有:
- 表的
Rows和Data_length是估算值,SHOW TABLE STATUS LIKE 't1'看到的rows可能偏差 30% 以上,尤其大表未ANALYZE TABLE时 -
mysql.server_cost或mysql.engine_cost表里某项被 DBA 改过,比如把key_compare_cost从默认0.05改成0.2,整个成本权重就偏移了 - InnoDB 实际读页可能命中 Buffer Pool,但成本模型仍按“磁盘随机读”计为 1.0,导致 I/O 成本高估
怎么验证成本计算逻辑:开 optimizer_trace 看每一步估算
这是唯一能看清优化器“怎么想”的方式,不是看结果,而是看它如何一步步算出那个 query_cost:
- 先执行
SET optimizer_trace="enabled=on",再跑你的 SQL,最后查SELECT * FROM information_schema.optimizer_trace - 在返回的
TRACE字段 JSON 中,重点找"analyzing_range_alternatives"和"rows_estimation"节点 - 里面会明确写出每个索引路径的成本分解,例如:
"range_scan_alternatives":[{"index":"idx_a","ranges":["a > 100"],"rows":1245,"cost":37.2}]—— 这个37.2就是走idx_a的总成本,含 I/O 和 CPU 分项 - 注意
"chosen_range_access_summary"下的"cost_for_plan",这才是最终被选中的成本值
哪些地方最容易被忽略:成本模型本身有隐含前提
成本数字看起来精确,但它建立在几个脆弱假设上:
- 所有列值均匀分布——但现实中
status字段可能 95% 是'active',优化器却按等值分布估算选择性,导致索引成本严重低估 - Buffer Pool 大小不影响成本计算——哪怕你配了 64G Buffer Pool,模型仍按“每次读都触发磁盘 IO”算 I/O 成本
- 连接顺序剪枝后,某些理论上更优的排列根本没进成本对比池——
optimizer_search_depth默认 62,但 8 张表 JOIN 时,8! = 40320种顺序,优化器只试其中一部分


















