EXPLAIN关键字段需重点关注:type为ALL/index表示全表/全索引扫描,key为NULL说明未走索引,Extra含Using filesort或Using temporary意味着排序/分组无法利用索引。

怎么看执行计划(EXPLAIN 输出关键字段)
MySQL 的 EXPLAIN 是分析慢 SQL 最直接的入口,但很多人只看 type 和 rows,漏掉真正致命的信号。重点盯住这几个字段:
-
type:出现ALL或index说明全表/全索引扫描,尤其ALL在大表上基本等于慢 SQL 定义;range算可控,ref/eq_ref才算走了有效索引 -
key:显示实际用到的索引名。为NULL就代表没走索引——别急着怪 SQL 写法,先确认该列是否真的有索引、索引顺序是否匹配WHERE条件 -
Extra:最危险的是Using filesort和Using temporary,意味着排序或分组无法利用索引完成,必须额外内存或磁盘操作;Using index是好现象(覆盖索引),Using index condition表示用了 ICP(索引条件下推)
为什么加了索引还是没走(常见索引失效场景)
索引存在 ≠ 被使用。以下写法会让优化器主动放弃索引:
- 对索引列做函数操作:
WHERE YEAR(create_time) = 2024→ 改成WHERE create_time >= '2024-01-01' AND create_time -
隐式类型转换:
user_id是INT,但写成WHERE user_id = '123'→ 字符串触发类型转换,索引失效 - LIKE 左模糊:
WHERE name LIKE '%abc'→ 无法使用 B+Tree 索引的有序性;LIKE 'abc%'可以 - 联合索引顺序错位:
INDEX(a,b,c),查询条件只有WHERE b = 1或WHERE b = 1 AND c = 2→ 无法命中该索引(最左前缀不满足)
如何模拟真实负载看执行流程(不只是单条 EXPLAIN)
EXPLAIN 只是预估,实际执行可能因数据分布、缓存、并发而不同。要验证真实行为,得结合:
- 开启慢日志并设置合理阈值:
SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 1;,再查slow_log表或日志文件,确认是否真被记录 - 用
SHOW PROFILE看各阶段耗时:SET profiling = 1; SELECT ... ; -- 你的慢 SQL SHOW PROFILES; SHOW PROFILE FOR QUERY 1;
重点关注Sorting result、Closing tables、Sending data这些阶段是否异常高 - 检查锁等待:
SELECT * FROM performance_schema.events_statements_current WHERE SQL_TEXT LIKE '%你的SQL%';配合sys.innodb_lock_waits视图看是否卡在行锁上
ORDER BY 和 GROUP BY 怎么避免 filesort / temporary
这两个操作最容易触发临时表和文件排序,性能杀手。核心原则:让排序/分组字段落在同一个索引的后缀位置,并且顺序一致。
-
ORDER BY a, b→ 索引要建为INDEX(a, b),不能是INDEX(b, a),也不能只建INDEX(a) -
GROUP BY a, b ORDER BY a, b→ 同样需要INDEX(a, b);如果还带LIMIT,索引能极大提升效率 - 注意 ASC/DESC 混用:
ORDER BY a ASC, b DESC在 MySQL 8.0 之前无法用单一索引优化(需分开建两个方向索引),8.0+ 支持INDEX(a ASC, b DESC) - 如果业务允许,把
GROUP BY提前聚合(比如用物化视图或定时汇总表),比实时计算安全得多
真实线上环境里,EXPLAIN 显示“走了索引”但响应仍慢,大概率是 rows 估算严重偏差(统计信息过期)、或者 Using filesort 占据了大部分时间——这两点最容易被忽略。











