EXPLAIN 不执行SQL而仅模拟优化器决策路径,基于统计信息估算执行计划;FORMAT=TREE比传统格式更准确展示嵌套结构与物化时机;EXPLAIN ANALYZE才反映真实执行情况,但需谨慎使用。

EXPLAIN 不是 MySQL 实时“分析 SQL”出来的
MySQL 的 EXPLAIN 不会真去执行你的语句,它走的是优化器的「预执行路径」:解析 SQL → 生成逻辑执行计划 → 基于统计信息和索引选择做成本估算 → 输出最终选定的物理执行计划。这个过程不碰数据行,也不加锁,所以快,但也不反映真实运行时行为(比如临时表膨胀、缓存命中率、并发争用)。
常见错误现象:EXPLAIN 显示用了 type=ref,但实际慢得像 ALL;或者 rows 预估 100,实际扫描 50 万行——大概率是统计信息过期或条件无法下推。
- 使用场景:调优前必跑,但别只看
Extra里有没有Using filesort或Using temporary,先确认rows和key_len是否合理 -
ANALYZE TABLE能强制刷新统计信息,对大表慎用(会锁表),优先考虑innodb_stats_persistent=ON+ 定期UPDATE统计 - 复合索引顺序影响极大:
WHERE a=1 AND b>10走(a,b)索引能用全,换成(b,a)就只能用上a的等值部分
为什么 EXPLAIN FORMAT=TREE 比传统格式更准
老式 EXPLAIN(FORMAT=TRADITIONAL)只给扁平化的一行一行,丢失嵌套关系和物化时机;FORMAT=TREE 是 8.0+ 默认输出,直接展示执行计划的树形结构,包括物化临时表、半连接展开、窗口函数分发等真实决策。
常见错误现象:用传统格式看不出子查询是否被物化,误以为是“相关子查询”,结果发现 FORMAT=TREE 里明确写着 materialized —— 其实早就转成哈希联接了。
- 必须用 MySQL 8.0.16+,低版本看不到
-> Materialize这类关键节点 -
FORMAT=TREE中的cost字段是优化器内部估算值,单位无意义,但可横向比:同一条 SQL 改写前后 cost 差 3 倍以上,基本说明改写有效 - 遇到
-> Filter: (t1.id > 100)出现在物化节点内部,说明过滤被下推到物化阶段,不是事后扫完再筛
EXPLAIN ANALYZE 才告诉你“实际发生了什么”
EXPLAIN ANALYZE 是真正执行语句并记录每一步耗时、行数、循环次数的调试模式,它补足了传统 EXPLAIN 的最大短板:预估 vs 真实偏差。但它有代价——会真实读数据、触发锁、消耗 I/O,不能在生产高峰期随便跑。
常见错误现象:看到 EXPLAIN 说 key_len=5,以为只用索引前两列,结果 EXPLAIN ANALYZE 显示 actual rows=120000,点开发现 filtered=0.01% —— 索引选对了,但 WHERE 条件区分度太差,大量回表后又被过滤掉。
- 只在开发/测试环境或低峰期使用;对写操作语句(
INSERT ... SELECT,UPDATE子查询)慎用,可能产生脏数据 - 关注
actual time和loops:如果某节点loops=1000且每次actual time都高,大概率是没走索引导致嵌套循环放大 -
EXPLAIN ANALYZE不显示 Buffer Pool 命中率、磁盘读等待,要结合SHOW PROFILE或 performance_schema 查
统计信息不准时,EXPLAIN 就是“一本正经胡说八道”
优化器依赖 INFORMATION_SCHEMA.STATISTICS 和 mysql.innodb_table_stats 里的页数、基数、直方图等数据做成本计算。一旦这些数据滞后(比如大表批量导入后没更新统计),EXPLAIN 选错索引、误判范围扫描、甚至放弃使用索引,都是常态。
常见错误现象:EXPLAIN 显示 type=range,但 key 是空的;或者明明有 WHERE status IN ('a','b'),却走了全表扫描——查 mysql.innodb_table_stats 发现 n_rows 还是三个月前的值。
- 手动更新:对单表用
ANALYZE TABLE t1;对库级批量处理,可用mysqlpump --skip-definer --no-create-info --skip-triggers配合脚本触发 - 直方图(8.0+)能显著改善非均匀分布字段的预估,但需显式创建:
ANALYZE TABLE t1 UPDATE HISTOGRAM ON status; -
innodb_stats_auto_recalc=ON并不可靠:只有当 10% 行变更才触发,默认阈值太大,高频小批量更新的表容易长期不准
真正难的不是看懂 EXPLAIN 输出,而是判断它哪一行可信、哪一行是统计失真导致的幻觉。尤其当 rows 和 filtered 同时异常时,先查统计信息,再动 SQL。










