MySQL 慢查询排查实战:EXPLAIN 看懂 type 与 Extra,一个字段定位性能问题
数据库变慢往往不是数据库本身"不行了",而是某条 SQL 走错了执行计划。本篇面向刚接触 MySQL 的开发者,手把手讲清楚怎么用 EXPLAIN 定位慢查询,重点解释 type 和 Extra 两个最容易读不懂的字段。读完你可以直接拿自己项目的慢 SQL 上手排查。
一、先找到慢 SQL 在哪
优化之前得先知道哪些 SQL 慢。MySQL 自带慢查询日志,默认可能没开。先确认:
sql
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';
如果 slow_query_log 是 OFF,可以临时打开(重启后失效,正式环境请写入配置文件):
sql
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1; -- 超过 1 秒的查询记日志
更推荐用另一条动态视图:information_schema.PROCESSLIST 里能直接看到正在执行、耗时长的语句:
sql
SELECT * FROM information_schema.PROCESSLIST
WHERE COMMAND = 'Query' AND TIME > 1;
拿到慢 SQL 后,下一步就是用 EXPLAIN 分析它为什么慢。
二、EXPLAIN 到底看什么
给任意 SELECT、UPDATE、DELETE 前面加 EXPLAIN,就能看到它打算怎么执行:
sql
EXPLAIN SELECT * FROM users WHERE email = 'demo@example.com';
输出是一张表,列比较多,初次看会懵。真正决定性能的核心列只有三个:type(访问类型)、key(实际用的索引)、Extra(附加信息)。下面逐个讲。
三、type:访问类型从好到坏
type 描述 MySQL 用什么方式找到记录,性能从好到坏大致是:
system > const > eq_ref > ref > range > index > ALL
逐个说明:
- const :主键或唯一索引的等值查询,最多命中一行。比如
WHERE id = 1。这是最快的结果之一。 - eq_ref :联表查询时,对关联表的每一行,用主键/唯一索引正好命中一行。常见于
JOIN。 - ref :用非唯一索引做等值匹配,可能命中多行。
WHERE email = ?且 email 有普通索引时通常是 ref。这是日常最健康的单表等值查询形态。 - range :索引范围扫描,
BETWEEN、>、<、IN会触发。比全表快,但要看范围大小。 - index:扫描整个索引树(不是全表,但也没省多少),比 ALL 略好。
- ALL :全表扫描,性能杀手。数据量大时一定要想办法优化掉。
一条经验法则:单表查询的 type 是 ALL,几乎必然有问题;是 ref 或 const,基本 OK。
四、Extra:附加信息里的危险信号
Extra 里会写很多提示,其中几个要特别警惕:
| Extra 内容 | 含义 | 处理方向 |
|---|---|---|
Using filesort |
排序无法用索引完成,需要额外排序 | 给 ORDER BY 列建索引 |
Using temporary |
用了临时表 | 检查 GROUP BY / DISTINCT 列是否有索引 |
Using index |
覆盖索引,直接从索引拿数据 | 这是好事,性能佳 |
Using where |
用 WHERE 过滤了索引扫描后的结果 | 正常,但配合 ALL 要留意 |
其中 Using filesort 和 Using temporary 是两块最常见的"隐藏性能税":它们意味着 MySQL 在内存里又排了一次序或建了一张临时表,数据量一大就会拖垮响应,甚至把临时表落到磁盘。
五、一个完整排查例子
假设有张用户表:
sql
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(64) NOT NULL,
city VARCHAR(32) NOT NULL,
created_at DATETIME NOT NULL,
INDEX idx_city (city)
);
业务里有条查询,按城市查最近注册的用户:
sql
EXPLAIN SELECT * FROM users WHERE city = '北京' ORDER BY created_at DESC LIMIT 20;
你会看到类似结果:
type: ref
key: idx_city
Extra: Using index condition; Using filesort
解读:
type=ref,key=idx_city:city走索引了,等值匹配没问题。Extra里有Using filesort:说明ORDER BY created_at没用到索引,MySQL 把命中的行再单独按created_at排了一次序。
如果 city = '北京' 命中几万行,这排序开销就很大。优化方法是建一个符合最左前缀的联合索引,让筛选和排序都走同一棵树:
sql
ALTER TABLE users ADD INDEX idx_city_created (city, created_at);
再看:
type: ref
key: idx_city_created
Extra: Using index condition
Using filesort 消失,排序直接顺着索引做,性能明显提升。
六、小结
EXPLAIN 是 MySQL 性能排查的第一利器,核心就三点:
- 用慢查询日志或
PROCESSLIST找到慢 SQL; - 看
type,发现ALL优先处理; - 看
Extra,警惕Using filesort和Using temporary,用联合索引消除它们。
下一步建议:打开你自己的项目,抓一条你觉得慢的查询,跑一遍 EXPLAIN,确认它的 type 和 Extra,再决定要不要加索引或改写。更多字段含义可查官方手册里 EXPLAIN 输出格式一节。