qinyelin
发布于 2026-09-03 / 18 阅读
0
0

MySQL 索引下推 ICP

面试频率:★★★★☆
工作频率:★★★★☆


⚡ 30 秒速记

ICP:

Index Condition Pushdown

中文:

索引条件下推 / 索引下推

一句话:

在使用二级索引查询时,把原本需要回表后才能进行的部分条件判断,下推到存储引擎的索引层,利用索引中已有的字段提前过滤数据,从而减少不必要的回表。

核心:

没有ICP:

二级索引
   ↓
找到候选记录
   ↓
回表
   ↓
拿完整数据
   ↓
判断其他条件


有ICP:

二级索引
   ↓
找到候选记录
   ↓
利用索引字段提前过滤
   ↓
符合条件
   ↓
回表

所以:

ICP核心作用

=

减少回表

EXPLAIN:

Using index condition

↓

使用了ICP

注意:

Using index
≠
Using index condition

一、什么是 ICP?

ICP 全称:

Index Condition Pushdown

中文:

索引条件下推

它是一种:

查询优化机制

核心思想:

能在索引层判断的条件,就尽量先判断,过滤掉不符合条件的数据以后,再对剩余记录进行回表。

目的:

减少回表

↓

减少数据页访问

↓

提高查询效率

二、先复习什么是回表

假设:

CREATE INDEX idx_name_age
ON user(name, age);

这是:

二级联合索引

叶子节点可以简单理解为:

name + age + 主键id

例如:

张三 18 → id=1

张三 20 → id=2

张四 30 → id=3

张五 20 → id=4

执行:

SELECT *
FROM user
WHERE name = '张三';

因为:

SELECT *

需要完整行数据。

而二级索引只有:

name
age
主键id

可能还缺少:

address
phone
email
...

所以:

二级索引
   ↓
得到主键id
   ↓
查询聚簇索引
   ↓
获取完整行

这个过程就是:

回表

三、没有 ICP 会怎么样?

假设联合索引:

INDEX(name, age)

查询:

SELECT *
FROM user
WHERE name LIKE '张%'
AND age = 20;

索引数据:

张三 18 → id=1

张三 20 → id=2

张四 30 → id=3

张五 20 → id=4

张六 25 → id=5

name LIKE '张%':

可以确定一个索引扫描范围

于是找到:

张三18

张三20

张四30

张五20

张六25

如果没有 ICP,可以粗略理解为:

根据name找到候选索引记录
        ↓
得到主键
        ↓
回表
        ↓
读取完整行
        ↓
再判断:

age = 20?

例如:

张三18
↓
回表
↓
age != 20
↓
丢掉


张三20
↓
回表
↓
age = 20
↓
返回


张四30
↓
回表
↓
age != 20
↓
丢掉

问题:

很多数据明明age不符合

↓

却已经回表了

产生:

大量不必要的回表

四、有 ICP 会怎么样?

还是:

SELECT *
FROM user
WHERE name LIKE '张%'
AND age = 20;

索引:

(name, age)

因为:

age

本身就在:

二级索引

里面。

所以 MySQL 可以:

先在索引层判断age

流程:

根据name找到候选索引记录
        ↓
直接读取索引里的age
        ↓
判断age = 20?
      ↙        ↘
    不是        是
     ↓          ↓
   丢掉        回表
                ↓
             完整数据

例如:

张三18
↓
索引层判断
↓
18 != 20
↓
直接丢掉
↓
不回表

张三20
↓
索引层判断
↓
20 = 20
↓
回表
↓
完整数据

张四30
↓
索引层判断
↓
30 != 20
↓
直接丢掉
↓
不回表

这样:

不符合age条件的数据

↓

根本不用回表

五、ICP 最大的作用

假设:

name LIKE '张%'

找到了:

10000条候选记录

其中:

age = 20

只有:

100条

没有 ICP

可以粗略理解:

10000条候选记录
       ↓
大量回表
       ↓
拿完整数据
       ↓
判断age
       ↓
最终100条符合

有 ICP

10000条候选索引记录
       ↓
索引层判断age
       ↓
过滤掉9900条
       ↓
剩100条
       ↓
只让符合条件的记录回表

所以:

ICP

↓

减少不必要的回表

这就是最核心的意义。


六、ICP 和联合索引有什么关系?

假设:

INDEX(name, age)

索引内部:

先按照name排序

↓

name相同再按照age排序

执行:

WHERE name LIKE '张%'
AND age = 20;

这里:

name

可以用于:

确定索引扫描范围

而:

age

虽然不一定能够继续缩小 B+ Tree 的扫描范围,但:

age就在索引里面

所以:

仍然可以拿来过滤

这就是:

ICP

七、为什么范围条件之后的字段不一定完全没用?

这是理解 ICP 非常重要的一点。

很多人学最左匹配以后会背:

范围查询之后

↓

后面的索引字段全部失效

这个说法太绝对。

例如:

INDEX(name, age)

查询:

WHERE name LIKE '张%'
AND age = 20;

可以理解:

name

↓

用于确定索引扫描范围

而:

age

↓

可能不能像连续等值匹配那样进一步缩小扫描范围

但是:

age还在索引里面

所以:

可以用于索引层过滤

从而:

减少回表

因此:

不能继续缩小索引扫描范围

≠

这个字段完全没用了

八、ICP 和最左匹配的区别

最左匹配主要解决:

哪些索引字段可以帮助确定 B+ Tree 的查找范围?

ICP 主要解决:

已经找到候选索引记录以后,能不能利用索引中的其他条件提前过滤,从而减少回表?

所以:

最左匹配
   ↓
确定查哪里
ICP
   ↓
候选数据里面
先过滤哪些

九、用一个例子理解

索引:

(name, age)

数据:

张三18

张三20

张四25

张五20

张六30

李四20

王五20

查询:

SELECT *
FROM user
WHERE name LIKE '张%'
AND age = 20;

第一步

根据:

name LIKE '张%'

找到:

张三18
张三20
张四25
张五20
张六30

这一步:

利用索引确定扫描范围

第二步

因为:

age

也在索引:

(name,age)

里面。

所以索引层直接判断:

18 ❌

20 ✅

25 ❌

20 ✅

30 ❌

剩下:

张三20

张五20

第三步

只对:

张三20

张五20

进行:

回表

获取:

完整行数据

十、为什么 ICP 能提升性能?

主要减少:

回表次数

回表意味着:

二级索引
   ↓
得到主键
   ↓
再次访问聚簇索引
   ↓
获取完整数据

如果:

候选记录很多

大量回表:

成本可能很高

ICP:

索引层提前过滤
   ↓
减少候选数据
   ↓
减少回表
   ↓
减少数据页访问

所以性能可能更好。


十一、EXPLAIN 怎么看 ICP?

执行:

EXPLAIN
SELECT *
FROM user
WHERE name LIKE '张%'
AND age = 20;

如果:

Extra

出现:

Using index condition

通常表示:

使用了ICP

也就是:

Index Condition Pushdown

十二、Using index 是什么?

你前面已经学过:

Using index

通常表示:

覆盖索引

例如:

INDEX(name, age)

执行:

SELECT name, age
FROM user
WHERE name = '张三';

需要:

name
age

索引:

name
age

全部都有。

所以:

直接从索引获取数据

不需要:

回表

可能看到:

Using index

十三、Using index condition 是什么?

Using index condition

表示:

ICP

流程:

索引找到候选记录
        ↓
索引层提前过滤
        ↓
符合条件
        ↓
再回表

所以:

Using index condition

并不代表:

完全不用回表

而是:

尽量先过滤,减少需要回表的数据。


十四、Using index 和 Using index condition 区别

这个面试很容易问。

Using index

覆盖索引
   ↓
查询需要的数据
索引里面全部都有
   ↓
直接返回
   ↓
通常不用回表

Using index condition

索引下推 ICP
   ↓
利用索引字段
提前过滤
   ↓
符合条件的数据
   ↓
再回表

所以:

Using index

↓

重点:
避免回表

而:

Using index condition

↓

重点:
减少回表

这句话非常好记:

覆盖索引:避免回表。

索引下推:减少回表。


十五、覆盖索引和 ICP 哪个更好?

例如:

INDEX(name, age)

查询:

SELECT name, age
FROM user
WHERE name LIKE '张%'
AND age = 20;

需要的数据:

name
age

索引里面:

全部都有

那么:

根本不需要获取完整行

可以直接:

从索引返回

这种:

覆盖索引

通常比:

先过滤
↓
再回表

更加直接。

所以:

能覆盖

↓

直接覆盖

ICP 更重要的场景通常是:

最终还是需要回表

↓

但是可以先过滤

↓

减少回表次数

十六、什么条件才能在索引层判断?

核心:

这个条件需要的数据

↓

必须能从当前索引记录中获得

例如索引:

(name, age)

可以在索引中直接获得:

name

age

主键

如果条件:

age = 20

索引里面有:

age

所以:

有机会在索引层判断

但是如果:

address = '杭州'

而索引:

(name, age)

里面没有:

address

那么:

索引层没有address

就不能靠这个索引直接完成:

address条件判断

通常还是需要:

回表
↓
拿address
↓
再判断

十七、ICP 主要用于二级索引

ICP 的价值主要体现在:

二级索引
   ↓
需要回表
   ↓
提前过滤
   ↓
减少回表

因为:

聚簇索引叶子节点

↓

本身就是完整行数据

而二级索引:

叶子节点不是完整行

所以:

减少回表

才特别有意义。


十八、完整流程对比

没有 ICP

联合二级索引
      ↓
找到10000条候选记录
      ↓
得到10000个主键
      ↓
大量回表
      ↓
拿完整数据
      ↓
判断age
      ↓
剩100条

有 ICP

联合二级索引
      ↓
找到10000条候选索引记录
      ↓
索引层判断age
      ↓
剩100条
      ↓
100条回表
      ↓
拿完整数据

核心区别:

没有ICP:

先回表
再过滤
有ICP:

先过滤
再回表

十九、和 B+ Tree 串起来

现在把前面的知识串起来:

联合索引
(name, age)
      ↓
B+ Tree
      ↓
根据name确定扫描范围
      ↓
扫描叶子节点
      ↓
索引里面还有age
      ↓
ICP判断age
      ↓
过滤不符合的数据
      ↓
得到主键
      ↓
回表
      ↓
聚簇索引
      ↓
完整行数据

所以:

B+ Tree

↓

联合索引

↓

最左匹配

↓

ICP

↓

减少回表

二十、和 EXPLAIN 串起来

查询:

EXPLAIN
SELECT *
FROM user
WHERE name LIKE '张%'
AND age = 20;

分析:

type
↓
看访问方式


key
↓
看使用哪个索引


rows
↓
看预计扫描多少


Extra
↓
看有没有:

Using index condition

如果:

Using index condition

说明:

使用ICP

二十一、一张图记住 ICP

                  二级索引
                     ↓
              找到候选索引记录
                     ↓
             索引中还有过滤条件?
                 ↙          ↘
               有            没有
                ↓              ↓
           索引层先判断       需要其他处理
                ↓
             符合条件?
             ↙      ↘
           不符合     符合
             ↓         ↓
           丢掉       回表
                       ↓
                   聚簇索引
                       ↓
                    完整数据

二十二、最重要的三个概念区别

回表

二级索引
   ↓
主键
   ↓
聚簇索引
   ↓
完整行

覆盖索引

查询需要的数据

↓

二级索引全部都有

↓

直接返回

↓

避免回表

ICP

最终还需要回表

↓

但是索引里有部分过滤条件

↓

索引层先过滤

↓

符合的才回表

↓

减少回表

一句话:

回表
=
去主键索引拿完整数据


覆盖索引
=
不用回表


ICP
=
少回表

二十三、完整知识链

现在 MySQL 索引这部分可以串成:

MySQL索引
    ↓
B+ Tree
    ↓
聚簇索引 / 二级索引
    ↓
二级索引查询
    ↓
需要完整数据
    ↓
回表

优化方式:

查询字段索引里全有
        ↓
覆盖索引
        ↓
避免回表

如果不能覆盖:

最终还是要回表
        ↓
索引中存在其他过滤字段
        ↓
ICP
        ↓
索引层提前过滤
        ↓
减少回表

所以:

覆盖索引

↓

最好:
直接不回表


ICP

↓

不能完全避免回表时:
尽量少回表

🎤 面试回答

问:

什么是索引下推 ICP?

答:

ICP 全称 Index Condition Pushdown,也叫索引条件下推。

在使用二级索引查询时,如果部分 WHERE 条件涉及的字段已经存在于当前索引中,MySQL 可以把这些条件下推到存储引擎的索引层进行判断。

这样可以先过滤掉不符合条件的索引记录,再对符合条件的数据进行回表,从而减少不必要的回表和数据页访问,提高查询效率。

在 EXPLAIN 中,如果 Extra 出现 Using index condition,通常表示使用了 ICP。


🎯 面试追问

Q1:ICP 全称是什么?

答:

Index Condition Pushdown

中文:

索引条件下推

Q2:ICP 主要解决什么问题?

答:

减少不必要的回表

Q3:ICP 为什么可以减少回表?

答:

因为:

索引里面已经存在部分WHERE条件需要的字段

↓

可以在索引层先判断

↓

不符合直接丢掉

↓

符合的才回表

Q4:没有 ICP 和有 ICP 有什么区别?

答:

没有ICP:

先回表
↓
再过滤
有ICP:

先在索引层过滤
↓
再回表

Q5:EXPLAIN 怎么看 ICP?

答:

看:

Extra

如果出现:

Using index condition

通常表示:

使用了ICP

Q6:Using index 和 Using index condition 一样吗?

答:

不一样
Using index

↓

通常表示覆盖索引

↓

避免回表
Using index condition

↓

ICP

↓

减少回表

Q7:ICP 是完全不回表吗?

答:

不是

ICP:

减少回表

覆盖索引:

可以避免回表

Q8:为什么 ICP 经常和联合索引一起出现?

答:

因为联合索引:

包含多个字段

有些字段:

用于确定索引扫描范围

其他索引字段:

可能还可以用于提前过滤

从而:

减少回表

Q9:不满足继续缩小索引范围的字段是不是完全没用?

答:

不一定

虽然它可能:

不能继续缩小B+ Tree扫描范围

但是如果字段:

存在于当前索引

可能:

通过ICP参与索引层过滤

Q10:索引里没有的字段能直接用于 ICP 过滤吗?

例如:

索引:

(name, age)

条件:

address = '杭州'

索引里面没有:

address

所以不能直接通过这个索引记录判断:

address条件

通常需要:

回表

获取完整数据后再处理。


⚠️ 易错点

1. ICP 不是覆盖索引

覆盖索引

=

避免回表
ICP

=

减少回表

不要混淆。


2. Using index condition 不是“不回表”

错误:

Using index condition

↓

不用回表

正确:

Using index condition

↓

索引层提前过滤

↓

减少回表

3. 后面的联合索引字段不一定完全没用

例如:

INDEX(name, age)

查询:

WHERE name LIKE '张%'
AND age = 20;

age 即使不能像连续等值匹配一样继续缩小扫描范围:

仍可能参与ICP过滤

所以不要死背:

范围查询以后

↓

后面所有字段彻底失效

4. ICP 的核心不是“让 SQL 走索引”

ICP 的核心是:

已经在使用索引查询候选记录

↓

尽量利用索引中的条件

↓

提前过滤

↓

减少回表

❓ 自测

  1. ICP 全称是什么?

  2. ICP 中文叫什么?

  3. ICP 主要解决什么问题?

  4. 什么是回表?

  5. 没有 ICP 时通常什么时候判断其他过滤条件?

  6. 有 ICP 时过滤发生在哪里?

  7. 为什么 ICP 可以减少回表?

  8. ICP 能完全避免回表吗?

  9. 什么机制可以避免回表?

  10. 覆盖索引和 ICP 有什么区别?

  11. Using index 通常表示什么?

  12. Using index condition 表示什么?

  13. Using index 和 Using index condition 一样吗?

  14. 联合索引为什么适合使用 ICP?

  15. INDEX(name,age) 中 age 不能继续缩小扫描范围时,是不是一定完全没用了?

  16. 什么情况下 age 还可能参与过滤?

  17. 索引中不存在的字段能直接通过当前索引记录进行 ICP 判断吗?

  18. 为什么减少回表可以提高性能?

  19. ICP 和最左匹配分别解决什么问题?

  20. EXPLAIN 怎么判断是否使用了 ICP?

  21. “覆盖索引避免回表,ICP 减少回表”这句话是什么意思?

  22. INDEX(name,age) 查询 name LIKE '张%' AND age=20 时,name 和 age 分别可能起什么作用?


评论