explain analyze 会真实执行sql并返回实际扫描行数、耗时等数据,而普通explain仅生成预估计划;它要求mysql≥8.0.18,生产环境需慎用。

EXPLAIN ANALYZE 和普通 EXPLAIN 的本质区别
普通 EXPLAIN 只生成预估执行计划,不真正执行 SQL;EXPLAIN ANALYZE 会真实执行查询,并返回实际扫描行数、真实耗时、各阶段执行时间等关键数据。它不是“增强版 EXPLAIN”,而是“带实测结果的执行计划快照”。
- 在压测或开发环境验证优化效果时,
EXPLAIN ANALYZE比EXPLAIN更可靠——比如你改了索引,EXPLAIN显示type=range,但rows=5000是预估;而EXPLAIN ANALYZE会告诉你“实际扫描了 4821 行,排序耗时 327ms” - 生产环境慎用:对大表
SELECT或带UPDATE/DELETE的语句执行EXPLAIN ANALYZE,会真实触发读写,可能阻塞业务或放大锁竞争 -
EXPLAIN ANALYZE要求 MySQL ≥ 8.0.18;低于此版本执行会报错ERROR 1064 (42000): You have an error in your SQL syntax
怎么看懂 EXPLAIN ANALYZE 的输出结构
它默认以树状格式(FORMAT=TREE)输出,比传统表格更直观反映嵌套操作和子节点开销。核心关注三类信息:
-
Actual time:格式为
actual_time_ms (startup_ms..total_ms),比如0.123..45.678表示该步骤启动延迟 0.123ms,总耗时 45.678ms。重点看total_ms占比高的节点 -
Rows examined:实际扫描的物理行数(不是返回行数),若远大于
rows列的预估值,说明统计信息过期,需ANALYZE TABLE t_name - Loops:该步骤被执行次数。例如嵌套循环 JOIN 中,内表扫描可能被外层驱动 1000 次,即使单次快,总耗时也高
示例片段:
-> Index lookup on t_order using idx_user_status (user_id = 10001, status = 2) (cost=12.40 rows=15) (actual time=0.082..0.215 rows=12 loops=1)这里
rows=12 是真实命中数,loops=1 表明没发生重复扫描,是健康信号。
哪些场景必须用 EXPLAIN ANALYZE 而非普通 EXPLAIN
当 EXPLAIN 给出的信息明显矛盾或无法解释性能现象时,EXPLAIN ANALYZE 是唯一能打破“黑盒猜测”的手段:
- 优化器选错索引:
possible_keys有多个,key却选了低效索引,EXPLAIN看不出原因;EXPLAIN ANALYZE可能暴露某索引因filtered=5%导致实际成本更高 - 隐式类型转换导致索引失效:SQL 写成
WHERE user_id = '10001'(字符串),而字段是INT,EXPLAIN显示type=ALL,但你不确定是否真因类型转换——EXPLAIN ANALYZE执行后若Rows examined等于全表行数,就坐实了 - 临时表或文件排序开销被低估:
EXPLAIN的Extra=Using filesort不告诉你排序花了多久;EXPLAIN ANALYZE会在对应节点显示actual time=1200.456..1200.456(即纯排序耗时 1.2 秒)
容易被忽略的实操细节
EXPLAIN ANALYZE 的真实执行特性决定了几个关键约束:
- 它会受事务隔离级别影响:在
REPEATABLE READ下,多次执行可能复用快照,Rows examined不变;切换到READ COMMITTED才能看到最新数据量变化 - 不能用于包含用户变量的语句,如
SELECT @row := @row + 1,会报错ERROR 1287 (HY000): 'SELECT @row' is not supported in EXPLAIN ANALYZE - 对
UNION查询,每个子句都会单独执行并计时,总耗时不等于各子句total_ms简单相加——因为存在合并结果集的额外开销,这部分体现在最外层节点 - 如果查询涉及分区表,
EXPLAIN ANALYZE会明确列出实际访问了哪些分区,比EXPLAIN的partitions列更可信
真正卡点往往不在语法或权限,而在“以为自己在看预估,其实已经在跑真实查询”。上线前拿 EXPLAIN ANALYZE 验证,务必确认目标表数据量级和当前负载——否则一次误操作可能让接口雪崩。











