EXPLAIN ANALYZE比普通EXPLAIN更准,因其真实执行查询并返回actual time、actual rows、loops等实测值,而普通EXPLAIN仅依赖统计信息预估,易因统计过期或隐式转换导致预估偏差。

EXPLAIN ANALYZE 是唯一能直接看到 SQL 实际执行开销的内置方式,但它会真实执行查询——别在生产库上对大表或写操作乱用。
EXPLAIN ANALYZE 为什么比普通 EXPLAIN 更准
普通 EXPLAIN 只靠统计信息和成本模型预估,比如它说 rows=100,但实际可能扫了 50 万行;而 EXPLAIN ANALYZE 真实跑一遍,输出里带 actual time、rows、loops 这些字段,全是实测值。
关键差异点:
-
actual time=0.012..0.456表示该算子单次迭代耗时范围(单位毫秒),..前是启动时间,后是总耗时 -
rows=9是真返回的行数,不是优化器猜的;如果和cost=... rows=...差距大(比如预估 10 行、实际 10000 行),说明统计信息过期或条件有隐式转换 -
loops=51很危险:表示这个子操作被重复执行了 51 次,常见于嵌套循环连接中驱动表返回 51 行,被当作外层循环次数
执行前必须确认的三件事
它不是“点一下就出报告”的工具,漏掉任一条件都可能白跑或出事:
- 确认 MySQL 版本 ≥ 8.0.18 ——
SELECT VERSION();,低于这个版本不支持ANALYZE子句 - 确保语句是只读
SELECT——EXPLAIN ANALYZE UPDATE ...在 8.0.19+ 才支持,且仍会真实修改数据 - 检查目标表是否有足够权限 —— 缺少
SELECT权限时会直接报错ERROR 1142 (42000): SELECT command denied,而不是静默跳过
怎么看懂输出里的性能瓶颈
输出是缩进树结构,从下往上读(叶子节点先执行);重点盯这些模式:
- 某行
actual time明显长于其他节点(比如 120ms vs 其他都 loops 值高 → 它就是热点,优先优化这一步(加索引 / 改写条件) - 出现
Table scan on t1但rows很大(如 239)且没走索引 → 检查WHERE是否用了函数、类型不匹配,或索引未覆盖查询字段 -
Sort或Using temporary类节点actual time高 →ORDER BY/GROUP BY没走索引,考虑添加复合索引或减少排序量 -
Nested loop inner join下面某子节点loops达到几百上千 → 驱动表结果集太大,换连接顺序或加过滤条件压驱动表输出行数
容易被忽略的副作用和限制
很多人以为“只是看执行计划”,其实它真实触发了整个查询生命周期:
- 会触发所有
BEFORE/AFTER触发器(如果是 DML 类语句)、锁行/锁表、产生 binlog(如果开启)、消耗 buffer pool 缓存 - 对大结果集,
actual time包含网络传输和客户端接收耗时,不是纯服务端执行时间 - 不支持
FORMAT=JSON,只接受FORMAT=TREE(默认可省略),试图加FORMAT=JSON会报错Unknown EXPLAIN format: 'JSON' - 如果查询本身超时(
wait_timeout或max_execution_time触发),EXPLAIN ANALYZE也会中断,并只输出已执行完的节点信息


















