EXPLAIN ANALYZE是MySQL 8.0.18引入的实测型执行分析命令,它真实执行SQL并返回每步的实际耗时、处理行数和循环次数,而普通EXPLAIN仅提供预估计划,无法反映真实性能偏差。
EXPLAIN ANALYZE 是唯一能告诉你“哪一步真慢、为什么慢、慢多少”的方式——它不是猜,是实测。
普通 EXPLAIN 只输出优化器的预估:比如显示 rows=500,但实际执行可能扫描了 32 万行;显示用了 key=idx_user_status,但因 WHERE u.status = 'active' 后还跟了 ORDER BY o.created_at DESC,而索引没覆盖排序字段,最终触发 Using filesort,耗时全砸在这一步上——这些,EXPLAIN 完全不暴露。
常见错误现象包括:
EXPLAIN 显示 type=ref,但查询仍慢 → 实际可能因 filtered 值极低(如 0.5%),导致大量回表Loops)远超预期Extra: Using temporary; Using filesort 就去加索引 → 但 EXPLAIN ANALYZE 可能显示临时表本身只耗 2ms,真正卡住的是某张表的磁盘读(Read: 1842 disk)执行 EXPLAIN ANALYZE SELECT ... 后,重点关注缩进结构中每行末尾的括号内容,以及 Summary 行:
actual time=0.042..198.631:区间表示该节点启动到结束的毫秒耗时,差值即纯执行时间。若某 Nested loop inner join 节点耗时占总时间 92%,说明连接逻辑或驱动表需重审Rows Removed by Filter: 99980:某表预估扫描 10 万行,实际仅保留 200 行 → 过滤条件未走索引,或字段选择性差(如 gender 只有 'M'/'F')Loops=127:驱动表返回 127 行,内层表被重复访问 127 次。若内层表无合适索引,等效于做 127 次全表扫描Buffers: shared hit=1248 read=42:read 值高说明大量数据未命中 buffer pool,优先考虑调大 innodb_buffer_pool_size 或减少扫描范围以 SELECT u.name, o.total, a.city FROM users u JOIN orders o ON u.id = o.user_id JOIN addresses a ON u.id = a.user_id WHERE u.status = 'active' AND o.created_at > '2026-03-01' 为例:
users 是最佳驱动表:若 users 中 status = 'active' 占比 95%,而 orders 上 created_at > '2026-03-01' 只返回 200 行,那 orders 才该当驱动表addresses 表若没在 user_id 上建索引,EXPLAIN ANALYZE 会显示其 actual time 在 Nested loop 内部飙升,且 Loops 值等于驱动表行数 × 1WHERE 条件跨表时(如加 a.city = 'Shanghai'),MySQL 可能被迫将过滤下推失败,导致先完成两表连接再过滤,此时 Rows Removed by Filter 会出现在最外层 Filter 节点,而非 addresses 访问节点EXPLAIN ANALYZE 本身不改性能,但它暴露的问题必须立刻验证:
Rows Removed by Filter 的字段,用 SHOW INDEX FROM table_name 确认索引是否包含该字段,且符合最左前缀;避免在 VARCHAR 字段上用 LIKE '%xxx' 还指望走索引Loops + 高 actual time 的内层表访问,手动强制驱动表顺序:在小表上加 STRAIGHT_JOIN,或改写为子查询 + JOIN 形式,再用 EXPLAIN ANALYZE 对比真实瓶颈往往藏在 actual time 和 Loops 的乘积里,而不是单看哪个数字最大;一次分析后不做索引调整或驱动表干预,等于白看。