🔍 1、简述
当你面对一个慢查询,不知从何下手优化时,
EXPLAIN
是你最值得信赖的工具。它能告诉你:MySQL 是如何执行 SQL 语句的?是否使用了索引?是否全表扫描? 本文将全面介绍 EXPLAIN 的用法、关键字段解读及实战样例。
🧠 2、什么是 EXPLAIN?
EXPLAIN
是 MySQL 提供的用于分析 SELECT、DELETE、INSERT、REPLACE、UPDATE 查询执行计划的命令。
通过执行:
EXPLAIN SELECT * FROM users WHERE id = 1;
MySQL 会返回一个表格,展示 SQL 语句的执行计划,有助于定位性能瓶颈。
2.1 EXPLAIN 输出字段详解
字段名 | 含义 |
---|---|
id | 执行计划中语句的序列 ID,越大越先执行 |
select_type | 查询类型,如 SIMPLE、PRIMARY、SUBQUERY 等 |
table | 当前访问的表名或临时表名 |
type | 连接类型,表示表的访问方式(非常关键) |
possible_keys | 可能使用的索引 |
key | 实际使用的索引 |
key_len | 索引长度,越短越优 |
ref | 索引比较的列或常量 |
rows | MySQL 估算需要读取的行数 |
Extra | 额外信息,如是否使用临时表、文件排序等 |
2.2 重点字段详解与优化建议
type
—— 最重要的性能指标
从好到差如下:
system > const > eq_ref > ref > range > index > ALL
🔹 ALL:全表扫描,性能最差,需优化
🔹 index:扫描索引全表
🔹 range:索引范围扫描(如 BETWEEN
, <
, IN
)
🔹 ref:使用非唯一索引等值匹配
🔹 eq_ref:使用唯一索引等值匹配(如主键)
🔹 const/system:单行常量匹配,最快
🧠 优化目标:避免 ALL,争取 eq_ref 或 range
Extra
常见值说明
Extra | 含义 | 建议 |
---|---|---|
Using where | 使用 where 过滤数据 | 可接受,但需考虑优化 |
Using index | 覆盖索引,无需回表 | 👍 高性能 |
Using filesort | 使用文件排序 | 建议加索引优化 |
Using temporary | 使用临时表 | 可能影响性能,建议优化 |
NULL | 没有额外操作 | 👍 最优 |
📦 3、实践样例分析
🌱 创建样本数据
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100),
age INT,
email VARCHAR(100),
INDEX idx_age (age),
INDEX idx_email (email)
);
INSERT INTO users (name, age, email)
SELECT
CONCAT('user', id),
FLOOR(RAND() * 100),
CONCAT('user', id, '@mail.com')
FROM (SELECT @id := @id + 1 AS id FROM information_schema.columns LIMIT 10000) AS t;
🧪 示例 1:全表扫描(低效)
EXPLAIN SELECT * FROM users WHERE name = 'user9999';
结果示例:
type | key | rows | Extra |
---|---|---|---|
ALL | NULL | 10000 | Using where |
说明:
🔹 未命中任何索引
🔹 type=ALL
,表示全表扫描
🔹 建议:对 name
添加索引
🧪 示例 2:索引等值匹配(推荐)
EXPLAIN SELECT * FROM users WHERE age = 30;
结果示例:
type | key | rows | Extra |
---|---|---|---|
ref | idx_age | 100 | Using where |
说明:
🔹 使用了 age
索引
🔹 扫描范围较小,性能良好
🧪 示例 3:索引范围扫描
EXPLAIN SELECT * FROM users WHERE age BETWEEN 20 AND 30;
结果示例:
type | key | rows | Extra |
---|---|---|---|
range | idx_age | 1000 | Using where |
说明:
🔹 使用了 range
类型扫描,属于较高性能操作
🧪 示例 4:覆盖索引优化
EXPLAIN SELECT email FROM users WHERE age = 25;
结果示例:
type | key | Extra |
---|---|---|
ref | idx_age | Using index |
说明:
🔹 查询字段在索引中 → 不用回表
🔹 Using index
表示使用了 覆盖索引
🧰 4、高级分析技巧
4.1 使用 EXPLAIN ANALYZE (MySQL 8.0+)
EXPLAIN ANALYZE SELECT e.* FROM employees e
JOIN departments d ON e.dept_id = d.dept_id
WHERE e.salary > 6000 AND d.dept_name = 'Engineering';
输出示例:
-> Nested loop inner join (cost=2.71 rows=1) (actual time=0.123..0.125 rows=1 loops=1)
-> Index lookup on d using PRIMARY (dept_id=e.dept_id) (cost=0.35 rows=1) (actual time=0.056..0.057 rows=1 loops=1)
-> Filter: (e.salary > 6000) (cost=2.36 rows=1) (actual time=0.065..0.066 rows=0 loops=1)
-> Index lookup on e using idx_dept_id (dept_id=d.dept_id) (cost=2.36 rows=5) (actual time=0.063..0.064 rows=5 loops=1)
4.2 优化器开关调试
-- 查看当前优化器开关
SELECT @@optimizer_switch;
-- 临时禁用某些优化
SET optimizer_switch='materialization=off';
EXPLAIN SELECT * FROM employees WHERE salary IN (SELECT salary FROM employees WHERE department='IT');
4.3 索引提示使用
-- 强制使用特定索引
EXPLAIN SELECT * FROM employees FORCE INDEX (idx_dept) WHERE department = 'IT' OR salary > 5000;
-- 忽略索引
EXPLAIN SELECT * FROM employees IGNORE INDEX (idx_salary) WHERE salary > 5000;
🛡️5、常见性能问题及解决方案
问题1:全表扫描(type=ALL)
-- 添加合适索引
ALTER TABLE employees ADD INDEX idx_age (age);
-- 重写查询
EXPLAIN SELECT * FROM employees WHERE age = 25; -- 替换 age > 20
问题2:文件排序(Using filesort)
-- 创建排序索引
ALTER TABLE employees ADD INDEX idx_dept_hire (department, hire_date);
-- 使用索引排序
EXPLAIN SELECT * FROM employees
WHERE department = 'IT'
ORDER BY hire_date DESC;
问题3:临时表(Using temporary)
-- 优化GROUP BY
EXPLAIN SELECT department, COUNT(*)
FROM employees
GROUP BY department; -- 确保GROUP BY使用索引
-- 添加适当索引
ALTER TABLE employees ADD INDEX idx_dept_name (department, name);
常见优化建议
场景 | 优化建议 |
---|---|
全表扫描 | 添加合适索引 |
Using filesort | 增加 ORDER BY 涉及列的复合索引 |
Using temporary | 拆分复杂查询或使用临时表手动优化 |
未使用预期索引 | 检查 WHERE 和 JOIN 条件,避免函数包裹字段 |
🔚 6、总结
指标 | 推荐值 |
---|---|
type | eq_ref / ref / range |
Extra | Using index、避免 filesort/temporary |
rows | 越小越好 |
key 与 possible_keys | 应尽量一致 |
✅ 工具推荐
🔹 Navicat / DBeaver:图形化 EXPLAIN 查看
🔹 MySQL Workbench:性能分析、索引建议
🔹 pt-query-digest:分析慢查询日志
如你希望,我可以帮你生成:
🔹 Spring Boot + MySQL 实战项目,集成 Druid 监控慢 SQL
🔹 自动化 SQL 分析脚本
🔹 基于 Explain 的优化报告模板
欢迎继续提问 🔍