EXPLAIN ANALYZE 会真实执行SQL并返回实测数据,而EXPLAIN仅做优化器预估;前者显示actual time、loops等运行时指标,后者仅输出预估rows和type等统计值。

EXPLAIN ANALYZE 和 EXPLAIN 有什么本质区别
关键就一条:EXPLAIN ANALYZE 会真实执行 SQL,而 EXPLAIN 只做预估。前者返回的是“实测数据”,后者是优化器拍脑袋算出来的统计值。
比如你看到 EXPLAIN 的 rows=1000,但实际可能扫了 50 万行;而 EXPLAIN ANALYZE 里 rows = 498231、loops = 1,你就知道这一步真干了快 50 万次 I/O。
-
actual time格式是start..end,总耗时 =end - start,单位毫秒 -
loops很容易被忽略:嵌套循环里,外层查出 N 行,内层就执行 N 次,总成本 =actual time × loops - MySQL 版本必须 ≥ 8.0.18,低于这个版本直接报错
Unknown syntax
怎么一眼看出哪步最拖慢查询
别看第一行,要看“子操作”的累计开销。尤其是 Nested Loop Inner Join 下面的 Index Lookup 或 Table Scan 行,它们的 actual time × loops 常常占整条 SQL 总耗时 90% 以上。
- 如果某行
loops = 5000且actual time = 0.02..0.15,那它实际贡献了约0.13 × 5000 ≈ 650ms -
Table scan on users出现且rows很大,说明没走索引,优先加索引或改 WHERE 条件 -
Index Range Scan的rows和loops都高,可能是范围条件太宽(如create_time > '2020-01-01'),需要收紧或加时间分区
常见陷阱:为什么 ANALYZE 结果看起来“更差”
不是查询变慢了,是你第一次看清了它本来有多慢。很多开发者用完 EXPLAIN ANALYZE 后才意识到:原来那个“还好”的查询,实际在生产环境每秒都在吃掉 200ms CPU。
- 缓存干扰:首次运行
EXPLAIN ANALYZE时,数据可能不在 buffer pool,第二次跑会快很多 —— 所以建议清空缓存后多跑几次取平均值:RESET QUERY CACHE;(旧版)或FLUSH TABLES;+ 关闭query_cache_type - 写操作阻塞:如果 SQL 包含
UPDATE或锁表动作,ANALYZE会真实加锁,影响其他连接 —— 切勿在高峰期对核心表直接跑 -
filtered字段在ANALYZE输出里不显示,但它的缺失会让优化器误判选择性;若发现rows远大于实际结果数,大概率是WHERE条件中用了函数(如YEAR(create_time) = 2025)导致索引失效
什么时候该停手,转去建索引或改 SQL
当看到以下任意一种组合,基本不用再调参,立刻建索引或重构逻辑:
-
type = ALL且rows > 10000(全表扫描超万行) -
Extra出现Using filesort或Using temporary,同时rows × loops > 50000 -
Index Lookup的loops等于外层Index Scan的rows,但内层rows是 1 —— 这说明驱动表选错了,应交换 JOIN 顺序或强制STRAIGHT_JOIN - 复合条件中,只有前导列命中索引(如索引是
(a,b,c),但 WHERE 只有b = ? AND c = ?),key_len显示为 0,key为NULL
真正难的不是看懂 EXPLAIN ANALYZE,而是判断哪一行的 loops 是设计使然、哪一行是索引没覆盖到位。这个分寸,得在慢日志 + ANALYZE + 实际数据分布之间来回比对才能拿准。


















