EXPLAIN 是 MySQL 中最重要的查询性能分析工具,它能显示 MySQL 如何执行 SQL 语句,包括访问表的方式、使用的索引、连接顺序等。通过分析 EXPLAIN 的输出,我们可以快速定位查询性能问题并进行优化。
✅ 一、如何使用 EXPLAIN
EXPLAIN SELECT * FROM users WHERE id = 1;
-- 或者更详细的分析(MySQL 8.0.18+)
EXPLAIN ANALYZE SELECT * FROM users WHERE id = 1;
✅ 二、EXPLAIN 输出字段详解
运行 EXPLAIN 后,会返回多行结果,每行代表一个表的访问计划。以下是各列的详细说明:
| 列名 | 说明 |
|---|---|
id |
查询的标识符,相同 id 表示同一查询计划,数字越大越先执行 |
select_type |
查询类型(如 SIMPLE、PRIMARY、SUBQUERY 等) |
table |
正在访问的表名 |
partitions |
匹配的分区(如果表已分区) |
type |
访问类型(性能从好到坏) |
possible_keys |
可能使用的索引 |
key |
实际使用的索引 |
key_len |
使用的索引长度(越短越好) |
ref |
与索引比较的列或常量 |
rows |
MySQL 估计需要扫描的行数(越少越好) |
filtered |
按表条件过滤后的行百分比 |
Extra |
额外信息(如 Using index、Using filesort 等) |
✅ 三、关键字段详解
🔹 type 列(访问类型)- 最重要
| 类型 | 性能 | 说明 |
|---|---|---|
system |
✅ 最好 | 表只有一行(系统表) |
const |
✅ 很好 | 通过主键或唯一索引查找,最多返回一行 |
eq_ref |
✅ 很好 | 多表连接时,使用主键或唯一索引 |
ref |
✅ 好 | 使用非唯一索引查找 |
range |
✅ 还行 | 使用索引进行范围查询(如 WHERE id > 10) |
index |
⚠️ 一般 | 全索引扫描(比全表扫描快,但仍有性能问题) |
ALL |
❌ 最差 | 全表扫描,应尽量避免 |
Extra 列(额外信息)- 性能优化关键
| 内容 | 说明 | 优化建议 |
|---|---|---|
Using index |
✅ 使用覆盖索引,无需回表 | 性能很好,无需优化 |
Using index condition |
✅ 使用索引条件下推(ICP) | 性能良好 |
Using where |
⚠️ 需要 MySQL 服务器层过滤 | 可能需要优化索引 |
Using filesort |
❌ 需要额外排序 | 重点优化对象 |
Using temporary |
❌ 需要临时表(如 GROUP BY) | 重点优化对象 |
Using join buffer |
⚠️ 使用连接缓冲区 | 可能需要优化连接顺序 |
🔹 rows 列
- MySQL 估计需要扫描的行数
- 数值越小越好
- 如果
rows接近表总行数,说明是全表扫描
✅ 四、实际示例分析
示例 1:良好性能(const + Using index)
EXPLAIN SELECT id, name FROM users WHERE id = 1;
id select_type table type key key_len ref rows Extra
1 SIMPLE users const PRIMARY 4 const 1 Using index
✅ 分析:
type: const- 主键查询,性能最优key: PRIMARY- 使用主键索引rows: 1- 只扫描 1 行Extra: Using index- 覆盖索引,无需回表
示例 2:中等性能(ref + Using where)
EXPLAIN SELECT * FROM orders WHERE user_id = 123;
id select_type table type key key_len ref rows Extra
1 SIMPLE orders ref idx_user_id 4 const 100 Using where
⚠️ 分析:
type: ref- 使用非唯一索引,性能还行key: idx_user_id- 使用了索引rows: 100- 扫描 100 行Extra: Using where- 索引无法覆盖所有字段,需回表 + 服务器层过滤
示例 3:性能问题(ALL + Using filesort)
EXPLAIN SELECT * FROM products ORDER BY price DESC;
id select_type table type key key_len ref rows Extra
1 SIMPLE products ALL NULL NULL NULL 10000 Using filesort
❌ 分析:
type: ALL- 全表扫描,性能很差key: NULL- 无索引可用rows: 10000- 扫描 10000 行Extra: Using filesort- 需要额外排序
✅ 优化建议:
CREATE INDEX idx_price ON products (price);
示例 4:多表连接(eq_ref + ref)
EXPLAIN SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id WHERE u.status = 'active';
id select_type table type key key_len ref rows Extra
1 SIMPLE u ref idx_status 767 const 100 Using where
1 SIMPLE o eq_ref PRIMARY 4 test.u.id 1 NULL
✅ 分析:
u表:type: ref- 通过status索引扫描o表:type: eq_ref- 通过主键连接,性能很好
✅ 五、EXPLAIN FORMAT 选项
1. 默认格式(表格)
EXPLAIN SELECT ...;
2. JSON 格式(更详细)
EXPLAIN FORMAT=JSON SELECT ...;
3. TREE 格式(MySQL 8.0.12+)
EXPLAIN FORMAT=TREE SELECT ...;
✅ 六、性能优化 checklist
使用 EXPLAIN 时重点关注:
| 检查项 | 标准 | 优化方向 |
|---|---|---|
type |
避免 ALL,尽量 ref 以上 |
创建索引 |
key |
应显示实际使用的索引 | 检查索引是否存在 |
rows |
越少越好(远小于表总行数) | 优化 WHERE 条件 |
Extra |
避免 Using filesort/Using temporary |
创建复合索引或覆盖索引 |
possible_keys |
应包含有效的索引 | 检查查询条件是否能使用索引 |
✅ 七、高级技巧
1. 使用 EXPLAIN ANALYZE(MySQL 8.0.18+)
EXPLAIN ANALYZE SELECT * FROM users WHERE id = 1;
显示实际执行时间、行数等统计信息。
2. 检查索引使用情况
-- 查看表的索引
SHOW INDEX FROM table_name;-- 查看表结构
SHOW CREATE TABLE table_name;
3. 使用 FORMAT=JSON 检查复杂查询
EXPLAIN FORMAT=JSON SELECT ...; -- 查看是否使用了 ICP、MRR 等优化技术
✅ 总结
EXPLAIN 是 MySQL 性能调优的必备工具,重点掌握:
type列:判断访问效率(const>ref>range>index>ALL)Extra列:发现性能瓶颈(如Using filesort、Using temporary)rows列:评估查询成本- 索引设计:
(WHERE字段, ORDER BY字段, SELECT字段)顺序