explain analyze会真实执行sql并返回各算子实际耗时、行数和循环次数,适用于mysql≥8.0.18的select语句,不可用于生产环境写操作,因其产生锁、临时表等副作用。

EXPLAIN ANALYZE 会真实执行 SQL,别在生产库直接跑
它不是“只看计划”的 EXPLAIN,而是真正走一遍查询流程,返回每个算子(如 Nested loop inner join、Index lookup)的 实际耗时、实际行数、循环次数。这意味着:
- 如果原 SQL 耗时 5 秒,
EXPLAIN ANALYZE至少也要 5 秒 —— 它不是快照,是实测 - 会产生真实锁、写入临时表、触发触发器、消耗 buffer pool,甚至影响正在运行的事务
- 对
UPDATE/DELETE类语句不支持(MySQL 报错ERROR 1288: The target table of the UPDATE is not updatable in EXPLAIN ANALYZE) - 只适用于
SELECT,且需 MySQL ≥ 8.0.18
对比 FORMAT=TRADITIONAL 和 FORMAT=TREE 的输出差异
传统表格格式(EXPLAIN FORMAT=TRADITIONAL)字段多但难看出嵌套逻辑;树形格式(EXPLAIN FORMAT=TREE)能看清算子层级,而 EXPLAIN ANALYZE 默认就是 FORMAT=TREE 带实际数据:
-
EXPLAIN FORMAT=TRADITIONAL SELECT * FROM users WHERE age > 25:返回 12 列表格,type是range,rows是预估 500 行 -
EXPLAIN ANALYZE SELECT * FROM users WHERE age > 25:输出中每行带(actual time=0.042..0.117 rows=6 loops=1),告诉你“真扫了 6 行,耗时 75ms” - 关键区别在
actual time(单位 ms)、rows(真实返回数)、loops(该算子被调用几次)—— 这三者合起来才能判断是不是“小结果集但高循环”导致慢
从输出里快速定位性能瓶颈的三个信号
不用逐行读完,盯住这三点就能判断问题在哪:
-
actual time 区间过大:比如
actual time=120.456..2890.123(差值超 2.8 秒),说明这个算子本身慢,优先检查是否缺失索引或统计信息过期 -
rows 远大于 filtered × 预估 rows:例如预估
rows=100、filtered=10.00,但actual rows=850,说明优化器严重误判数据分布,ANALYZE TABLE users可能立刻见效 -
loops 明显异常:比如
Index lookup on orders using idx_user_id (cost=2.35 rows=1) (actual time=0.010..0.010 rows=1 loops=500),意味着驱动表返回了 500 行,被反复查了 500 次 —— 这是典型的 JOIN 顺序/条件写法问题,不是加索引能解决的
为什么有时候 EXPLAIN ANALYZE 比原查询还慢?
它额外做了三件事:
- 开启 performance_schema 的迭代器监控(即使你没开相关 consumer)
- 为每个算子记录微秒级时间戳,涉及频繁系统调用
- 强制收集完整执行路径统计,禁用部分短路优化(比如不会因
LIMIT 1就提前终止扫描)
尤其当查询本身依赖缓存(如 query cache 已废弃,但 OS page cache 或 InnoDB buffer pool 有热数据)时,EXPLAIN ANALYZE 第一次跑会更慢;第二次可能快些,但依然比裸查多 5%~15% 开销。真正要压测响应时间,还是得用 BENCHMARK() 或应用层打点。











