详解索引下推

索引下推(Index Condition Pushdown,ICP)

MySQL 5.6 版本引入,InnoDB / MyISAM 都支持,默认开启。

概括:

在索引遍历过程中,把 WHERE 里能用到索引字段的条件,直接在索引层过滤,减少回表次数。

一、没有 ICP 的时候怎么做

假设联合索引:idx_name_age (name, age) 表结构:

sql 复制代码
CREATE TABLE user(
    id INT PRIMARY KEY,
    name VARCHAR(20),
    age INT,
    phone VARCHAR(11),
    INDEX idx_name_age(name, age)
);

查询 SQL:

sql 复制代码
SELECT * FROM user WHERE name='张三' AND age>18;

无 ICP(5.6 之前)执行流程

联合索引叶子节点保存 (name, age, 主键id)

  1. 根据 name='张三' 在索引树找到所有匹配name='张三'的索引记录;
  2. 不判断 age 条件,拿着这些记录里的主键 id,全部回表,读取完整行数据;
  3. 回到服务层,再过滤 age>18;
  4. 返回结果。

问题:很多name='张三'但是age<=18的数据,白白做了回表,IO 开销大。

开启 ICP 之后

  1. 根据 name='张三' 在索引树找到匹配索引记录;
  2. 在索引层直接判断索引字段条件 age>18;
  3. 只有索引记录同时满足 name 和 age,才拿主键 id 去回表;不满足直接丢弃;
  4. 回表读取完整行,返回。

✅ 核心优化点:在索引页就过滤掉一部分数据,减少回表次数。

二、ICP 适用条件

  1. 只能用于二级索引,主键索引不生效(主键索引本身就存完整数据,不存在回表)
  2. WHERE 条件里,部分字段在索引中,部分不在索引中
  3. 条件是范围查询之后的索引列(联合索引最左匹配,范围后面的字段无法用于索引查找,但可以用 ICP 过滤)
  4. 不能是聚合函数、子查询;
  5. SELECT 只查索引字段(覆盖索引)时,不需要回表,ICP 就没意义。

❗ 关键区分:

  • 索引查找(Index seek) :用来定位索引起始位置,只能用最左前缀,遇到范围> < between就停止;
  • 索引下推 ICP :范围后面的索引字段,不能用来定位起点,但可以用来过滤。

三、经典场景示例

场景 1:联合索引 + 范围查询(ICP 最典型场景)

索引:idx_a_b_c(a, b, c) SQL:

sql 复制代码
SELECT * FROM t WHERE a=10 AND b>20 AND c=30;

联合索引最左匹配规则: a=10 等值,b>20 范围,b 之后的 c 无法参与索引查找。

  1. a=10:等值匹配 → 定位到所有 a=10 的这一段索引区间 ✅
  2. b>20:范围查询 在 a=10 这一组里,找到第一个 b>20 的位置,然后顺序向后扫描一段连续索引 。 ⚠️ 重点:经过 b>20 这个范围之后,这一段扫描出来的数据里,b 不再是一个固定值,b 是 21、22、23...... 不断变化。

    原本只有当 b 固定不变的时候,c 才是有序的;现在 b 五花八门,c 在这段范围里不再全局有序!

联合索引里,c 的有序性,仅建立在前面所有字段全部相等的前提下。

  • a 固定,b 固定 → c 有序
  • a 固定,b 是一个范围(多个不同 b)→ c 无序

索引想要快速定位 c=30,必须 c 是有序的,才能二分查找。 现在 c 乱序了,无法利用索引快速过滤 c,只能:

通过索引找到 a=10 AND b>20 的所有行,回表取出完整数据,再在内存里过滤 c=30

对比演示,更好理解

索引 idx(a,b,c)

  1. where a=10 and b=20 and c=30 a 等值,b 等值,c 等值 → 三列全部走索引定位 ✅
  2. where a=10 and b>20 and c=30 a 等值,b 范围 → b 之后 c无法索引定位,只能扫描后过滤 c ❌
  3. where a=10 and b=20 and c>30 a 等值,b 等值,c 范围 → c 是第一个范围,c 后面无字段,没问题 ✅

优化方案

如果业务经常 a=xx AND b>xx AND c=xx,

调换索引顺序: 把等值条件放范围前面:idx_a_c_b(a,c,b) 条件:a=10 AND c=30 AND b>20

  • a 等值,c 等值,b 范围 等值列全部放在范围列之前,就可以充分利用索引。

没有 ICP: 找到所有 a=10, b>20 的索引记录 → 全部回表 → 在 MySQL 服务层过滤 c=30。

开启 ICP: 找到 a=10, b>20 的索引记录 → 在索引层直接判断 c=30,不满足直接丢掉,只把满足 c=30 的记录回表。

这里 c 在范围 b> 后面,不能用于索引定位,但是可以 ICP 过滤。

看执行计划 explain,Extra 字段会出现 Using index condition,代表 ICP 生效。

sql 复制代码
Extra: Using index condition

区分:

Using index:覆盖索引,不需要回表;

Using index condition:索引下推 ICP; 两个可以同时出现。

场景 2:ICP 不生效的场景

① 条件字段不在索引里

索引 idx_a_b(a,b)

sql 复制代码
SELECT * FROM t WHERE a=1 AND b>2 AND d=5; -- d不在索引

d=5 无法下推,只能回表后过滤;只有 a、b 可以在索引层处理。

② 覆盖索引,不需要回表
复制代码
SELECT a,b,c FROM t WHERE a=10 AND b>20 AND c=30;

查询字段全部在索引里,不需要回表。此时 ICP 没有收益,Extra 不会出现 Using index condition。

③ 主键索引

主键索引叶子节点保存整行数据,本身没有回表操作,ICP 无效。

④ 子查询、函数操作
复制代码
SELECT * FROM t WHERE a=10 AND b>20 AND DATE(c)='2026-01-01';

DATE(c) 函数,字段发生隐式转换,ICP 无法下推。

四、ICP 的局限性

  1. ICP 只是过滤,不能减少索引扫描行数(explain 里的 rows 是预估扫描索引行数,ICP 不会减少这个 rows,只会减少回表次数)
  2. ICP 只能过滤索引上存在的字段,非索引字段条件必须回表后过滤
  3. 对于 InnoDB,ICP 只支持二级索引;
  4. ICP 无法优化排序、分组。

五、开关控制

复制代码
-- 查看是否开启ICP
show variables like 'optimizer_switch';

-- 关闭ICP(测试对比用)
set optimizer_switch = 'index_condition_pushdown=off';
-- 开启ICP
set optimizer_switch = 'index_condition_pushdown=on';

六、一句话总结面试版

索引下推 ICP 是 MySQL 5.6 引入的优化,针对二级索引。

当联合索引遇到范围查询后,后面的索引字段不能用来定位索引起点,但可以在索引页提前过滤数据,减少回表次数,降低随机 IO;

执行计划 Extra 显示Using index condition代表生效。ICP 不改变索引扫描行数,只减少回表。

七、面试常问对比

  1. ICP 和覆盖索引的区别?

ICP:减少回表次数;覆盖索引:完全不需要回表。二者可以同时存在。

  1. ICP 会不会减少 explain 里的 rows?

不会,rows 是预估索引扫描行数,ICP 只是扫描到索引记录时提前过滤,不减少索引扫描数量。

相关推荐
泡海椒4 小时前
深度解析JQuick-SQL AST到逻辑计划转换机制|分布式SQL原理
数据库·分布式·sql
心静财富之门4 小时前
Python 数据共享四种方案完整实例(同进程 / JSON / Redis / HTTP)
数据库·python·oracle
略略略咯咯4 小时前
啊啊啊啊啊
java·服务器·数据库
奇牙coding5 小时前
GPT-5.5 报 401 invalid_api_key 怎么办?换密钥前按 4 层排查请求头、项目权限和端点
数据库·gpt·ios·ai
IvorySQL5 小时前
PostgreSQL 日报| WAL summarizer 断言失败(10 月 26 日)
数据库·postgresql·区块链
这个DBA有点耶6 小时前
2026年国产数据库“实际用户增长最快”核验指南:口径优先级、可查数据与选型动作清单
数据库·mysql·架构
程序员塔塔喵6 小时前
【MySQL篇|第三篇】脏读,幻读,不可重复读
数据库·mysql·不可重复读·幻读·脏读
数据库小学妹6 小时前
OLAP数据库怎么选、怎么优化?路线、提速与边界
运维·数据库·后端
织码weavecodes7 小时前
新闻资讯模块设计:内容发布、阅读计数与附件鉴权
java·服务器·数据库
小羊没烦恼!7 小时前
分布式事务 | 使用DTM 的Saga 模式
java·linux·运维·数据库·wpf