EXPLAIN ANALYZE会真实执行SQL并返回每步的实际耗时、行数和循环次数,而非预估;必须避免在生产大表或高峰期使用,且不支持事务回滚。

EXPLAIN ANALYZE 会真实执行 SQL,别在生产环境乱跑
它不是“只看计划”,而是真查、真排序、真 Join,还会把每一步的耗时、实际返回行数、循环次数都打出来。你看到的 actual time=0.141..7.787 rows=125 loops=1 是实测数据,不是估算。
常见错误现象:开发在压测环境跑 EXPLAIN ANALYZE SELECT * FROM orders WHERE created_at > '2020-01-01',结果查了 300 万行,拖慢整个实例。
- 必须确认目标表数据量小,或加
LIMIT 10缩小范围(注意:加 LIMIT 会影响执行计划,仅用于快速验证) - 避免在高峰期对大表执行,尤其带
ORDER BY或GROUP BY的语句 -
EXPLAIN ANALYZE不支持事务回滚 —— 它执行的是真实 DML(如DELETE),删了就是删了
关键字段怎么看:actual time、rows、loops 是核心
传统 EXPLAIN 的 rows 是优化器预估,而 EXPLAIN ANALYZE 的 rows 和 actual time 是实测值,二者偏差大就说明统计信息过期或选择率误判。
示例片段:
-> Filter: (world.city.Population > 1000000) (cost=4.53 rows=6) (actual time=0.030..0.131 rows=2 loops=51)
这里 rows=6 是预估,rows=2 是真实返回行数,loops=51 表示该 Filter 被执行了 51 次(因为外层 Nested Loop 迭代了 51 次),actual time=0.030..0.131 是单次执行耗时范围。
-
actual time格式是启动耗时..总耗时,单位毫秒;若相差过大(如 0.01..120.5),说明该步骤存在严重延迟波动 -
loops高通常意味着嵌套深或驱动表过大,比如loops=10000的 Index lookup 很可能成为瓶颈 - 对比
rows和filtered字段:若预估rows=1000但实际rows=1,且filtered=0.1,说明条件选择性极好,但优化器没识别出来
和 FORMAT=TREE 搭配看,才能理清执行顺序
EXPLAIN FORMAT=TREE 输出树状结构,反映算子层级和依赖关系;EXPLAIN ANALYZE 默认用传统表格,但加上 FORMAT=TREE 才能把实际耗时嵌入树中,看清哪一层最拖后腿。
命令写法:
EXPLAIN FORMAT=TREE ANALYZE SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id WHERE o.status = 1;
输出里你会看到类似:
-> Nested loop inner join (cost=... rows=...) (actual time=... rows=... loops=...) -> Index lookup on u using PRIMARY (cost=... rows=...) (actual time=... rows=... loops=...) -> Filter: (o.status = 1) (cost=... rows=...) (actual time=... rows=... loops=...)
- 缩进代表执行依赖:内层算子必须等外层产出结果才能开始
- 同一级多个子节点(如两个 Index lookup),是并行准备还是串行执行,取决于引擎是否启用并行扫描(MySQL 8.0 默认不开启)
- 如果某层
actual time明显高于其他同级节点,优先检查它的索引覆盖、数据局部性或锁等待
EXPLAIN ANALYZE 不解决所有问题,但能暴露估算失真
它不能告诉你“为什么没走索引”,但能告诉你“明明有索引,为什么还是扫了 10 万行”。这时候要立刻去看 SHOW INDEX FROM table_name 和 ANALYZE TABLE table_name 是否执行过。
- 当
key列为空但possible_keys有值,且actual time奇高,大概率是统计信息陈旧,ANALYZE TABLE后再试 - 若
type=ALL且rows接近表总行数,但actual time却很短,说明数据在 buffer pool 里全热,掩盖了真实 IO 压力 —— 这时候得看Handler_read_*状态变量 -
EXPLAIN ANALYZE对子查询、CTE、窗口函数的支持较完整,但对临时表(Using temporary)的实际内存/磁盘开销不体现,需结合performance_schema查memory_summary_by_thread_by_event_name
真正难的不是看懂那几行 actual time,而是判断“这个耗时到底是 CPU、IO 还是锁导致的” —— EXPLAIN ANALYZE 只给结果,不给归因。


















