EXPLAIN 输出里的 cost 字段到底靠不靠谱
MySQL 8.0.19+ 的
EXPLAIN FORMAT=TREE和
EXPLAIN FORMAT=JSON会显示
cost值,但它不是真实执行耗时,而是优化器基于统计信息估算的「I/O + CPU」相对代价。这个值只在同一条 SQL 的不同执行路径间有比较意义,跨语句、跨表、跨版本基本不可比。
cost不含网络传输、锁等待、并发竞争等运行时开销 统计信息过期(
ANALYZE TABLE没跑)会导致 cost 严重失真 小表全表扫描 cost 可能比大表走索引还低——因为优化器认为随机 I/O 比顺序扫更贵
真正影响执行计划选择的关键参数
优化器不是只看 cost,还会受以下硬性规则和阈值驱动:
eq_range_index_dive_limit:当
IN列表超过该值(默认 200),优化器跳过索引统计采样,直接按“全范围扫描”估算,容易误判走全表
range_optimizer_max_mem_size:控制范围扫描估算内存上限,超限后退化为粗略估算,cost 偏离实际 索引基数(
Cardinality)不准 →
rows预估错误 → cost 计算崩盘 隐式类型转换(如
WHERE varchar_col = 123)强制放弃索引,但
EXPLAIN仍可能显示“Using index”,cost 却很低——这是假象
手动干预执行计划前必须验证的三件事
别急着加
FORCE INDEX或改写 SQL,先确认底层是否真有问题: 用
SHOW INDEX FROM table_name查
Cardinality是否接近真实行数;偏差 >30% 就要
ANALYZE TABLE执行
SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE,找
"best_covering_index"和
"condition_filtering_pct",看优化器是否被过滤条件误导 对比
EXPLAIN ANALYZE(MySQL 8.0.18+)的真实执行树,看哪一步的
actual_time远超
estimated_cost对应的预期
EXPLAIN ANALYZE SELECT * FROM orders WHERE status = 'shipped' AND created_at > '2024-01-01';
cost 估算失效的典型信号
当出现以下任一现象,说明 cost 已不可信,得靠观测而非估算做决策:
EXPLAIN显示走了索引,但
profiling或
performance_schema.events_statements_history显示
Handler_read_next高到离谱 相同 SQL 在从库上走索引,在主库上走全表(主从统计信息未同步)
rows预估是 100,实际扫描 50 万行(
Handler_read_rnd_next爆增) 加了
USE INDEX后响应时间反而翻倍——说明优化器原本选的路径虽 cost 高,但实际更稳(比如避免临时表/文件排序)
优化器的 cost 是一张粗糙的地图,不是导航软件。它依赖统计信息的鲜度、配置参数的合理性、以及你没写的那些隐式假设。真正决定快慢的,永远是磁盘寻道次数、缓冲池命中率、和那条没被
EXPLAIN显示出来的锁等待链。
