覆盖索引与索引下推:Explain 执行计划中的 Using Index 是怎么来的
大家好,我是程序员卷卷狗。在用EXPLAINExtra 字段含义覆盖索引(Covering Index)索引下推(Index Condition Pushdown,ICP)但很多开发者只知道它们“说明走索引”,哪种情况能出现?具体加速了哪一步?和“回表”有什么关系?本篇就带你彻底搞清楚这两个优化机制的底层逻辑。当 SQL 查询所需的所有列都能从索引中直接获取,无需回表访问聚簇索引时,就叫覆盖索引。
文章目录
《覆盖索引与索引下推:Explain 执行计划中的 Using Index 是怎么来的》
一、前言:理解 Explain 中的 “Using Index”
大家好,我是程序员卷卷狗。
在用 EXPLAIN 分析 SQL 执行计划时,你一定见过这两种常见输出:
| Extra 字段 | 含义 |
|---|---|
| Using index | 覆盖索引(Covering Index) |
| Using index condition | 索引下推(Index Condition Pushdown,ICP) |
但很多开发者只知道它们“说明走索引”,
却不清楚:
- 哪种情况能出现?
- 具体加速了哪一步?
- 和“回表”有什么关系?
本篇就带你彻底搞清楚这两个优化机制的底层逻辑。
二、覆盖索引(Covering Index)——避免回表的关键
(1)定义
当 SQL 查询所需的所有列都能从索引中直接获取,
无需回表访问聚簇索引时,就叫覆盖索引。
举例说明
CREATE TABLE user (
id INT PRIMARY KEY,
name VARCHAR(20),
age INT,
INDEX idx_name_age(name, age)
);
执行:
EXPLAIN SELECT name, age FROM user WHERE name = '卷卷狗';
输出(关键部分):
type: ref
key: idx_name_age
Extra: Using index
这意味着——所有数据都能在
idx_name_age这棵索引树中直接拿到,无需回表。
(2)执行过程对比
| 查询方式 | 访问路径 | IO 次数 |
|---|---|---|
| 非覆盖索引 | 先查二级索引 → 再回主键索引 | 2 次 |
| 覆盖索引 | 只查二级索引即可 | 1 次 |
少一次回表 = 少一次磁盘 IO,性能直接提升一倍以上。
(3)覆盖索引的本质
覆盖索引其实是一种冗余设计:
- 你在索引中冗余了一些字段;
- 让查询只靠这棵索引树就能完成。
所以设计联合索引时,建议遵守:
最左前缀原则 + 查询列尽量被索引包含。
(4)判断方法
执行:
EXPLAIN SELECT * FROM user WHERE name = '卷卷狗';
输出:
Extra: Using where
说明需要回表(因为 * 包含了非索引字段 id)。
而:
EXPLAIN SELECT name, age FROM user WHERE name = '卷卷狗';
输出:
Extra: Using index
→ 覆盖索引命中成功。
三、索引下推优化(Index Condition Pushdown, ICP)
(1)问题背景
在 MySQL 5.5 之前,索引过滤条件的判断逻辑是这样的:
从索引中取出主键值
→ 回表取整行数据
→ 在 Server 层做 where 条件过滤
这意味着:
即使部分数据在索引阶段已经能排除,也会被“提前回表”——
浪费了大量 IO。
(2)优化原理
从 MySQL 5.6 开始引入 ICP(Index Condition Pushdown) 优化。
它把一部分 WHERE 条件下推到存储引擎层,在访问索引时直接过滤。
执行过程对比:
| 阶段 | ICP 前(旧版) | ICP 后(优化) |
|---|---|---|
| 过滤位置 | Server 层 | 存储引擎层 |
| 回表次数 | 每条索引记录都回表 | 仅匹配条件的索引记录回表 |
| 性能提升 | 低 | 显著 |
(3)示例
EXPLAIN SELECT * FROM user WHERE name LIKE '卷%' AND age > 20;
输出:
Extra: Using index condition
执行流程如下:
- 先从
idx_name_age索引树中按 name 前缀定位; - 在索引层(InnoDB 内部)先判断 age > 20;
- 只对符合条件的记录执行“回表”。
减少了回表次数,大幅降低 IO。
(4)支持条件
ICP 能生效需满足:
- 查询使用二级索引;
- WHERE 条件中部分字段在索引中;
- 查询未完全覆盖索引(否则已是覆盖索引)。
ICP 是覆盖索引和普通索引之间的一种折中优化。
四、Explain 实战对比
| SQL | Extra 字段 | 含义 |
|---|---|---|
SELECT name FROM user WHERE name='卷卷狗'; |
Using index | 覆盖索引(无需回表) |
SELECT * FROM user WHERE name='卷卷狗'; |
Using where | 普通索引(需要回表) |
SELECT * FROM user WHERE name LIKE '卷%' AND age>20; |
Using index condition | 索引下推(部分过滤提前) |
五、设计索引时的优化建议
-
尽量让查询走覆盖索引
- SELECT 列尽量只包含索引字段;
- 业务常用字段放入联合索引末尾。
-
WHERE 过滤字段放在索引前缀
- 遵守最左匹配原则;
- 便于优化器利用 ICP。
-
避免过宽索引(太多字段)
- 虽然能覆盖,但会占用大量内存与缓存页。
-
适当使用联合索引而非多个单列索引
- 单列索引无法同时命中多个条件;
- 联合索引可利用顺序性和 ICP。
六、面试高频问题与答题模板
| 问题 | 答案要点 |
|---|---|
| Q1:什么是覆盖索引? | 查询字段被索引完全覆盖,无需回表。 |
| Q2:覆盖索引的好处? | 减少一次回表 IO,性能提升显著。 |
| Q3:什么是索引下推? | 把部分 WHERE 条件下推至存储引擎层提前过滤。 |
| Q4:覆盖索引和索引下推能同时出现吗? | 不能。覆盖索引已无需回表,ICP 针对未完全覆盖的查询。 |
| Q5:Explain 的 “Using index” 和 “Using index condition” 有什么区别? | 前者是完全命中索引;后者是部分条件提前过滤。 |
| Q6:ICP 从哪个版本开始支持? | MySQL 5.6。 |
七、总结
索引优化并不止“建索引”这么简单,
关键是理解存储引擎层做了什么。
一句话记住:
“Using index” = 不回表;
“Using index condition” = 少回表。
掌握这两者,你就能读懂 Explain 输出背后的逻辑,
写出真正高效的 SQL。
下一篇(第 8 篇),我将写——
《联合索引的最左前缀原则与失效场景》,
讲清楚联合索引的排序规则、范围查询陷阱、函数操作导致失效的底层原理。
更多推荐



所有评论(0)