面试频率:★★★★★
工作频率:★★★★★
⚡ 30 秒速记
EXPLAIN 用来查看:
MySQL 准备如何执行一条 SQL。
例如:
EXPLAIN
SELECT *
FROM user
WHERE name = '张三';
常见结果:
id
select_type
table
partitions
type
possible_keys
key
key_len
ref
rows
filtered
Extra
刚开始不用全部背。
先重点掌握:
type
possible_keys
key
rows
Extra
简单记:
type
↓
怎么查
possible_keys
↓
可能用哪些索引
key
↓
实际选择哪个索引
rows
↓
预计检查多少行
Extra
↓
额外执行信息
分析顺序:
EXPLAIN
↓
type
↓
key
↓
rows
↓
Extra
一、EXPLAIN 是什么?
平时执行:
SELECT *
FROM user
WHERE name = '张三';
我们可能会猜:
会不会走idx_name?
会不会全表扫描?
会扫描多少数据?
会不会回表?
不要靠猜。
可以:
EXPLAIN
SELECT *
FROM user
WHERE name = '张三';
让 MySQL 告诉我们:
它准备怎么执行这条SQL
所以:
EXPLAIN 是分析 SQL 执行计划的重要工具。
二、先看哪几个字段?
刚开始重点掌握:
type
possible_keys
key
rows
Extra
以后看到:
EXPLAIN结果
先按照:
type
↓
key
↓
rows
↓
Extra
分析。
三、possible_keys
possible_keys 表示:
MySQL 优化器认为这条 SQL 可能使用哪些索引。
例如:
possible_keys = idx_name
说明:
优化器发现
idx_name
可能可以用于这条SQL
但是:
possible_keys有索引
不代表:
最终一定使用这个索引
四、key
key 表示:
MySQL 最终实际选择使用的索引。
例如:
possible_keys = idx_name
key = idx_name
说明:
候选:
idx_name
↓
最终:
idx_name
也就是:
实际使用了idx_name
如果:
key = NULL
通常说明:
没有选择索引
可能:
全表扫描
但最终还要结合:
type
一起看。
五、possible_keys 和 key 的区别
这个面试经常问。
possible_keys
=
理论上可能使用的索引
key
=
最终实际选择的索引
例如:
possible_keys = idx_name, idx_age
key = idx_name
说明:
优化器发现:
idx_name
idx_age
都有可能
但是最终认为:
idx_name成本更低
所以:
选择idx_name
六、判断有没有走索引看哪个?
重点看:
key
不要只看:
possible_keys
因为:
possible_keys有值
可能:
key = NULL
也就是:
虽然存在候选索引
↓
但是优化器最终没选
七、type 是什么?
type 是 EXPLAIN 中非常重要的字段。
表示:
MySQL 使用什么方式访问表中的数据。
常见访问类型可以先这样记:
system
↓
const
↓
eq_ref
↓
ref
↓
range
↓
index
↓
ALL
通常:
越往上
↓
扫描范围越小
越往下
↓
扫描范围越大
注意:
这是一个方便记忆的常见顺序,不要机械理解成任何情况下上面的都绝对比下面快,实际性能还要结合数据量、rows、回表、SQL 等一起判断。
八、const
例如:
SELECT *
FROM user
WHERE id = 10;
假设:
id
是:
PRIMARY KEY
MySQL 可以通过:
主键索引
快速确定:
唯一一条数据
可能看到:
type = const
可以简单理解:
通过主键 / 唯一索引
↓
等值查询
↓
最多匹配非常少的数据
性能通常很好。
九、ref
例如:
CREATE INDEX idx_name
ON user(name);
查询:
SELECT *
FROM user
WHERE name = '张三';
因为:
name
是普通索引。
可能存在:
张三 id=10
张三 id=20
张三 id=50
也就是说:
一个name
↓
可能对应多条数据
可能:
type = ref
可以简单理解:
普通非唯一索引的等值查询中经常看到 ref。
十、range
例如:
SELECT *
FROM user
WHERE id > 100;
或者:
SELECT *
FROM user
WHERE id BETWEEN 100 AND 200;
可能:
type = range
表示:
索引范围扫描
可以理解:
找到100附近
↓
沿索引继续扫描
↓
直到200
这和我们之前学的:
B+ Tree叶子节点有序
正好对应。
十一、index
如果:
type = index
可以简单理解:
MySQL 扫描整个索引。
注意:
index
不代表:
性能一定很好
因为:
整个索引
可能也非常大。
例如:
1000万条索引记录
↓
全部扫描
一样可能很慢。
但是索引页通常比完整数据行更小,所以:
全索引扫描
某些情况下可能比:
全表扫描
成本低。
十二、ALL
这个一定要认识。
type = ALL
表示:
全表扫描。
例如:
SELECT *
FROM user
WHERE address = '杭州';
假设:
address没有索引
MySQL 可能:
第1行
↓
判断address
第2行
↓
判断address
第3行
↓
...
整张表
所以:
type = ALL
十三、看到 ALL 一定有问题吗?
不一定。
例如:
表里面只有20条数据
全表扫描:
20条
可能比:
走索引
+
回表
还简单。
所以:
type = ALL
不是看到就一定要优化。
还需要看:
rows
例如:
type = ALL
rows = 20
可能:
问题不大
但:
type = ALL
rows = 5000000
就需要:
重点关注
十四、type 怎么记?
先重点记这几个:
const
↓
主键 / 唯一索引等值查询常见
ref
↓
普通索引等值查询常见
range
↓
索引范围查询
index
↓
扫描整个索引
ALL
↓
扫描整张表
面试中通常:
至少尽量达到range
是一个常见的经验说法。
但是:
不能只根据 type 一个字段判断 SQL 性能。
十五、rows
rows 表示:
MySQL 优化器估计为了完成查询需要检查的行数。
例如:
rows = 10
说明预计:
检查大约10行
而:
rows = 1000000
说明预计:
检查大量数据
通常:
rows越小
↓
需要检查的数据越少
十六、rows 是真实扫描行数吗?
不是。
一定要记:
rows
=
优化器估算值
不是:
真实执行后精确扫描行数
所以:
rows = 10000
不是说:
一定刚好扫描10000行
十七、为什么 rows 很重要?
例如两条执行计划:
方案 A:
type = ref
key = idx_name
rows = 10
方案 B:
type = ALL
key = NULL
rows = 1000000
明显:
方案A
↓
预计检查的数据少很多
方案 B:
全表扫描
+
预计100万行
就值得重点关注。
十八、Extra
Extra 表示:
MySQL 执行 SQL 时的一些额外信息。
常见:
Using index
Using where
Using index condition
Using temporary
Using filesort
刚开始重点认识:
Using index
Using where
Using index condition
十九、Using index
例如:
CREATE INDEX idx_name_age
ON user(name, age);
查询:
SELECT name, age
FROM user
WHERE name = '张三';
查询需要:
name
age
索引:
(name,age)
已经全部包含。
所以:
不需要回表
可能:
Extra = Using index
通常表示:
查询需要的数据可以直接从索引中获取,也就是使用了覆盖索引。
二十、Using where
如果:
Extra = Using where
表示:
MySQL 还需要根据 WHERE 条件对读取到的记录进行过滤。
可以简单理解:
先读取候选数据
↓
再判断WHERE条件
↓
符合
↓
返回
注意:
Using where
不代表:
一定没走索引
可能:
key = idx_name
Extra = Using where
也就是:
使用了索引
+
还需要进行WHERE过滤
二十一、Using index condition
如果:
Extra = Using index condition
表示:
使用了ICP
Index Condition Pushdown
索引条件下推
简单理解:
原来:
索引找到候选记录
↓
回表
↓
再判断某些条件
ICP:
索引找到候选记录
↓
在索引层先过滤一部分
↓
减少不必要的回表
这个后面可以单独学习。
现在先记:
Using index condition
↓
索引下推 ICP
二十二、Using index 和 Using index condition 不一样
这个非常容易混。
Using index
通常:
覆盖索引
↓
查询数据直接从索引获得
↓
避免回表
而:
Using index condition
表示:
ICP索引下推
↓
在索引层提前过滤
↓
减少回表
所以:
Using index
≠
Using index condition
二十三、完整案例1:主键查询
SQL:
EXPLAIN
SELECT *
FROM user
WHERE id = 10;
可能:
type = const
possible_keys = PRIMARY
key = PRIMARY
rows = 1
分析:
possible_keys = PRIMARY
↓
主键索引可以使用
key = PRIMARY
↓
实际使用主键索引
type = const
↓
唯一等值定位
rows = 1
↓
预计只检查极少数据
整体:
很好
二十四、完整案例2:普通索引
索引:
CREATE INDEX idx_name
ON user(name);
SQL:
EXPLAIN
SELECT *
FROM user
WHERE name = '张三';
可能:
type = ref
possible_keys = idx_name
key = idx_name
rows = 5
分析:
type = ref
↓
普通索引等值查询
key = idx_name
↓
实际使用idx_name
rows = 5
↓
预计匹配少量数据
二十五、完整案例3:范围查询
SQL:
EXPLAIN
SELECT *
FROM user
WHERE id BETWEEN 100 AND 200;
可能:
type = range
key = PRIMARY
rows = 100
分析:
PRIMARY
↓
使用主键索引
range
↓
范围扫描
rows
↓
预计检查一定范围的数据
二十六、完整案例4:全表扫描
SQL:
EXPLAIN
SELECT *
FROM user
WHERE address = '杭州';
假设:
address没有索引
可能:
type = ALL
possible_keys = NULL
key = NULL
rows = 1000000
Extra = Using where
分析:
type = ALL
↓
全表扫描
key = NULL
↓
没有使用索引
rows = 1000000
↓
预计检查100万行
Using where
↓
还要判断address条件
这种:
值得重点关注
二十七、分析 EXPLAIN 的顺序
以后不要看到一堆字段就乱。
直接按照:
第一步:
type
↓
怎么访问数据?
然后:
第二步:
key
↓
实际使用哪个索引?
然后:
第三步:
rows
↓
预计检查多少行?
最后:
第四步:
Extra
↓
有没有覆盖索引?
有没有额外过滤?
有没有ICP?
可以记:
type
↓
key
↓
rows
↓
Extra
二十八、看到什么情况要警觉?
例如:
type = ALL
key = NULL
rows = 3000000
意味着:
全表扫描
+
没有使用索引
+
预计检查300万行
这种通常:
值得重点分析
但是:
type = ALL
key = NULL
rows = 10
表只有:
10条数据
那么:
可能完全没必要优化
所以:
EXPLAIN 一定要结合数据量分析,不能机械背规则。
二十九、EXPLAIN 和索引失效的关系
之前我们学:
函数
隐式类型转换
LIKE前导%
最左匹配
低选择性
这些只是告诉你:
可能影响索引使用
最终到底有没有走:
不要猜
直接:
EXPLAIN SQL;
然后看:
type
key
rows
Extra
所以:
索引理论
↓
判断可能情况
↓
EXPLAIN
↓
验证实际执行计划
三十、和前面知识串起来
现在 MySQL 索引这条线:
B+ Tree
↓
为什么索引查询快
↓
聚簇索引 / 二级索引
↓
回表
↓
覆盖索引
↓
联合索引
↓
最左匹配
↓
索引失效
↓
EXPLAIN
↓
验证SQL到底怎么执行
以后拿到一条 SQL:
SELECT age
FROM user
WHERE name = '张三';
索引:
INDEX(name, age)
你可以分析:
第一层:
name满足最左匹配
↓
可以利用联合索引定位
第二层:
name + age都在索引
↓
覆盖索引
↓
不用回表
第三层:
EXPLAIN
↓
验证执行计划
这样整个知识就串起来了。
🎤 面试回答
问:
EXPLAIN 是干什么的?
答:
EXPLAIN 用来查看 MySQL 的 SQL 执行计划,可以帮助我们分析 SQL 的访问方式、可能使用的索引、实际使用的索引、预计扫描行数以及其他执行信息。
分析 EXPLAIN 时我通常会重点关注:
type
possible_keys
key
rows
Extra
其中:
type
表示访问方式;
possible_keys
表示可能使用的索引;
key
表示最终实际选择的索引;
rows
表示优化器预计需要检查的行数;
Extra
可以看到覆盖索引、额外过滤、索引下推等信息。
🎯 面试追问
Q1:possible_keys 和 key 有什么区别?
答:
possible_keys
↓
可能使用的索引
key
↓
最终实际选择的索引
Q2:判断有没有走索引主要看什么?
答:
key
如果:
key = NULL
通常说明:
没有选择索引
还要结合:
type
判断访问方式。
Q3:type = ALL 是什么意思?
答:
全表扫描
Q4:type = index 是什么意思?
答:
扫描整个索引
注意:
index
≠
一定很快
因为:
可能扫描大量索引记录
Q5:type = range 是什么意思?
答:
索引范围扫描
例如:
WHERE id BETWEEN 100 AND 200;
Q6:type = ref 是什么意思?
答:
普通非唯一索引:
等值查询
中经常出现。
例如:
WHERE name = '张三';
Q7:type = const 是什么意思?
答:
主键或唯一索引:
等值查询
并且可以快速确定极少数据时经常出现。
Q8:rows 是实际扫描行数吗?
答:
不是
它是:
优化器估算需要检查的行数
Q9:Using index 是什么意思?
答:
通常表示:
覆盖索引
↓
查询需要的数据可以直接从索引取得
↓
避免回表
Q10:Using where 是什么意思?
答:
表示:
读取数据以后
↓
还需要根据WHERE条件过滤
它不代表:
一定没使用索引
Q11:Using index condition 是什么意思?
答:
表示:
ICP
Index Condition Pushdown
索引条件下推
作用:
在索引层提前过滤
↓
减少不必要的回表
Q12:Using index 和 Using index condition 一样吗?
答:
不一样
Using index
↓
通常表示覆盖索引
Using index condition
↓
表示索引下推ICP
Q13:看到 type = ALL 就一定要优化吗?
答:
不一定
还要看:
rows
表的数据量
查询场景
如果:
表只有几十条数据
全表扫描可能完全没问题。
⚠️ 易错点
1. possible_keys 有值 ≠ 实际走索引
例如:
possible_keys = idx_name
key = NULL
说明:
idx_name虽然可以作为候选
↓
但优化器最终没有选择
2. key 才是最终选择的索引
所以判断:
实际走哪个索引
重点看:
key
3. type = index 不是普通意义上的“走索引就很快”
它表示:
扫描整个索引
所以:
1000万条索引记录
↓
还是可能扫描很多数据
4. type = ALL 不一定就是垃圾 SQL
如果:
表非常小
全表扫描:
可能就是最优方案
5. rows 不是精确值
rows
=
优化器估算
不是:
实际扫描行数
6. Using where 不代表没走索引
可能:
key = idx_name
Extra = Using where
说明:
使用索引
+
仍然需要WHERE过滤
7. EXPLAIN 不能只看一个字段
不要:
看到key有值
↓
SQL一定很好
也不要:
看到ALL
↓
SQL一定很差
应该综合:
type
key
rows
Extra
数据量
SQL业务场景
判断。
❓ 自测
-
EXPLAIN 是干什么的?
-
EXPLAIN 最重要的几个字段有哪些?
-
possible_keys 是什么意思?
-
key 是什么意思?
-
possible_keys 和 key 有什么区别?
-
判断实际使用哪个索引主要看哪个字段?
-
key = NULL 通常代表什么?
-
type 是什么意思?
-
type = const 是什么意思?
-
type = ref 是什么意思?
-
type = range 是什么意思?
-
type = index 是什么意思?
-
type = ALL 是什么意思?
-
index 和 ALL 有什么区别?
-
type = ALL 一定需要优化吗?
-
rows 是什么意思?
-
rows 是真实扫描行数吗?
-
rows 很大意味着什么?
-
Extra 是干什么的?
-
Using index 是什么意思?
-
Using where 是什么意思?
-
Using where 是否代表没有走索引?
-
Using index condition 是什么意思?
-
Using index 和 Using index condition 有什么区别?
-
为什么 possible_keys 有索引,key 还可能是 NULL?
-
为什么字段有索引,优化器还可能不使用?
-
分析 EXPLAIN 推荐按照什么顺序?
-
type=ALL、key=NULL、rows=3000000为什么值得关注? -
为什么不能只根据 type 判断 SQL 性能?
-
EXPLAIN 和索引失效有什么关系?