基础概念
索引下推(Index Pushdown, ICP) 是数据库查询优化中的一种核心技术,用于减少回表次数、提升查询效率,尤其适用于联合索引场景。
索引下推是指:数据库在遍历索引时,提前过滤掉不满足查询条件的记录,只将符合条件的记录回表获取完整数据。
简单来说,就是在索引层面完成部分 WHERE 过滤,而非全部等到回表后再过滤。

索引下推就是将原本应该在Server层执行的任务下推到引擎层执行。
不使用索引下推
这是 MySQL 在未开启 ICP 优化时的传统查询流程。
1)索引扫描与定位
- MySQL 查询优化器决定使用某个索引来执行查询。
- 存储引擎开始扫描该索引,逐条读取索引记录。索引记录中包含索引列的值以及对应的主键(或指向数据行的指针)。
2)回表
对于每一条从索引中找到的记录,存储引擎立即 使用其中的主键值去主键索引(聚簇索引)中查找并读取完整的行数据。这个过程就是 "回表"。
3)Server 层过滤
- 存储引擎将读取到的完整行数据返回给 MySQL 的 Server 层。
- Server 层接收到完整行后,再根据
WHERE子句中的所有条件对该行数据进行过滤。 - 如果满足条件,则将其加入结果集;如果不满足,则丢弃。
核心特点: 索引仅用于快速定位到可能的行,但所有 WHERE 条件的过滤工作都在 Server 层完成。这意味着,即使某些 WHERE 条件可以通过索引列判断,数据库也必须先把完整的行拉取出来才能进行判断。
使用索引下推
ICP 是一种优化,它允许存储引擎在扫描索引的同时,提前过滤掉一部分不符合条件的数据,从而减少回表次数。
1)索引扫描
同样,优化器决定使用某个索引,存储引擎开始扫描该索引,逐条读取索引记录。
2)存储引擎内过滤
- 在读取了一条索引记录后,存储引擎不会立即去回表。
- 它会检查
WHERE子句中,是否有可以利用当前索引中的列进行判断的条件。 - 如果存在这样的条件,存储引擎会在存储引擎层内部直接用索引记录中的值对该条件进行判断。
如果判断结果不满足条件:存储引擎会直接丢弃这条索引记录,不再进行后续的回表操作,而是直接处理下一条索引记录。
如果判断结果满足条件 :说明这条索引记录对应的完整行有可能是符合查询要求的,才会进入下一步。
3)回表
只有通过了存储引擎内部过滤的索引记录,存储引擎才会使用其主键值去回表,读取完整的行数据。
4)Server 层最终过滤
- 存储引擎将完整的行数据返回给 Server 层。
- Server 层会对该行数据进行最后的检查,以满足
WHERE子句中那些无法在存储引擎层利用索引进行过滤的剩余条件(例如,过滤条件涉及非索引列,或者是函数操作等)。 - 最终决定是否将该行加入结果集。
核心特点: 一部分 WHERE 过滤工作被 "下推" 到了存储引擎层,并利用索引中的数据提前完成。这减少了不必要的回表操作,因为那些明显不满足部分 WHERE 条件的记录在索引扫描阶段就被过滤掉了,根本不会去读取完整行。
实例

这是一个基础的学生信息表,我设立了**(name, age)**的联合索引。

这是索引树叶子节点具体的数据图,接下来从图出发说明用ICP和不用ICP的区别。
select语句为:SELECT * FROM students_contact WHERE name LIKE '张%'AND age > 18;
不用ICP:
范围查询之后的索引会失效,所以生效的索引只有name LIKE '张%'。因此联合索引拿到数据后会不断回表查询,获得所有符合张%的数据,然后再返回server层对age > 18进行筛选。

最终过滤出来只有张三一条是符合条件的,上述过程联合索引的字段age是没有任何作用的。
使用ICP:
存储引擎根据(name,age)联合索引,找到name LIKE '张%',由于联合索引中包含age列,所以存储引擎直接再联合索引里按照age > 18过滤。按照过滤后的数据再一一进行回表扫描。

可以看到,如果使用索引下推的话,只需要一次回表,在存储引擎层面就可以根据联合索引筛选出想要的数据。
我们同样也可以使用Explain来验证我们的想法。
EXPLAIN SELECT * FROM students_contact
WHERE name LIKE '张%' -- 1. 索引前缀范围扫描
AND age > 18 -- 2. 索引内非前缀条件 (ICP 过滤)

可以从执行计划看到,Extra 中显示 Using index condition,即使用了联合索引。
注意如果发现一直没有用上索引下推,需要检查下功能是否被禁用,默认是打开的。可以通过设置系统变量optimizer_switch控制:index_condition_pushdown。
# 打开索引下推 SET optimizer_switch = 'index_condition_pushdown=on';
# 关闭索引下推 SET optimizer_switch = 'index_condition_pushdown=off';
适用场景
- 联合索引 :必须是多列索引,且过滤条件包含索引中非前缀的列(如上述例子中的
age、city)。 - 非前缀匹配查询 :如
LIKE '张%'(前缀匹配)+ 其他列过滤,或范围查询(age > 20)。 - 回表代价高:当索引前缀匹配到大量记录时,ICP 能显著减少回表次数。