EXPLAIN FORMAT=JSON 中的 cost 是优化器基于统计信息估算的相对代价,单位为等价随机I/O次数,并非真实执行耗时;它不反映CPU、内存、锁等待等运行时开销,仅用于内部路径选择,DBA调优应重点关注 rows、key 和实际 Rows_examined 的对比。

EXPLAIN FORMAT=JSON 里的 cost 是估算值,不是真实执行开销
cost 字段出现在 EXPLAIN FORMAT=JSON 输出的 query_cost 或各节点的 cost_info 中,它由 MySQL 优化器基于统计信息(如索引基数、表行数、页大小)和固定代价模型算出,单位是“随机 I/O 次数等价值”。这个值不反映 CPU、内存或网络消耗,也不包含锁等待、刷脏页、Buffer Pool 缓存命中率波动等运行时因素。
常见误解是把它当“毫秒”或“执行耗时”,实际中:同一语句多次 EXPLAIN 可能返回不同 cost(统计信息更新后),但真实执行时间未必同步变化;加了 WHERE 条件后 cost 下降,却因索引失效导致实际变慢——这时得看 rows_examined 和 key 是否被用上。
-
cost主要用于优化器内部做 join 顺序、访问路径选择,对 DBA 调优参考价值有限 - 对比不同执行计划时,
cost差异 > 20% 才值得关注;小差异大概率是统计误差 - 若
cost极低但查询卡住,优先检查锁、长事务、磁盘 I/O 延迟,而非怀疑优化器算错
rows_examined 是真实扫描行数,但只在 profiling 开启后才准确
注意:EXPLAIN FORMAT=JSON **本身不输出 rows_examined**。这个字段实际来自 SHOW PROFILE、performance_schema.events_statements_summary_by_digest,或开启 slow_query_log 并设置 log_queries_not_using_indexes = ON 后的慢日志中的 Rows_examined 列。
真正和 EXPLAIN 直接关联的是 rows(JSON 中为 rows_estimated)——它是优化器对“该节点将扫描多少行”的预估,而 rows_examined 是执行后 MySQL 计数器累加的真实值。两者差距大,往往意味着:
- 统计信息过期(
ANALYZE TABLE未及时运行) - 使用了函数/表达式导致索引无法范围扫描(如
WHERE YEAR(create_time) = 2023) - 多表 JOIN 时驱动表选择错误,引发笛卡尔积放大
- 存在隐式类型转换(如字符串字段与数字比较),使
key显示为NULL但rows_examined暴涨
如何用 rows 和 key 配合判断索引是否真生效
在 EXPLAIN FORMAT=JSON 的每个 table 节点里,重点看三处:rows(预估扫描行数)、key(实际选用索引名)、key_length(索引使用长度)。仅 key 非 NULL 不代表高效——可能只是用到了索引最左前缀,但后续条件仍需回表或全索引扫描。
典型陷阱:
-
key有值,rows却接近全表行数 → 索引区分度差(如性别字段建索引),或WHERE条件用了LIKE '%abc' -
key_length小于索引定义长度(如联合索引(a,b,c),key_length=4表示只用到了a)→ 后续b/c条件未走索引 -
rows很小但执行慢 → 可能Extra里有Using filesort或Using temporary,说明排序/分组没走索引
别信 cost,盯紧 rows + key + 实际 Rows_examined
线上调优最有效的组合是:先用 EXPLAIN FORMAT=JSON 看 rows 和 key 是否合理,再查慢日志或 performance_schema 拿到真实 Rows_examined,最后比对二者差距。如果 rows 是 1000 但 Rows_examined 是 500000,基本可以确定统计信息不准或查询写法触发了优化器误判。
这时候改 SQL 比调优器参数更有效:拆分复杂条件、避免在索引列上运算、用 FORCE INDEX 临时验证路径、或者补上缺失的复合索引。MySQL 8.0+ 的直方图功能(ANALYZE TABLE ... UPDATE HISTOGRAM)能缓解部分 rows 估算偏差,但覆盖不了所有场景。










