qinyelin
发布于 2026-09-03 / 1 阅读
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 indexUsing 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 分别可能起什么作用?


评论