Mysql:索引下推

**索引下推(Index Condition Pushdown,简称 ICP)**是一种减少回表次数的优化

它的核心思想是:

在读取完整数据行之前,先利用索引中已有的列判断一部分 WHERE 条件;只有满足条件的索引记录,才继续回表读取完整行

MySQL 官方文档将它描述为:存储引擎先检查索引元组,只有索引条件满足时才读取完整表记录

一、 没有索引下推时

假设有表:

复制代码
CREATE TABLE people (
    id BIGINT PRIMARY KEY,
    zipcode CHAR(5),
    lastname VARCHAR(50),
    firstname VARCHAR(50),
    address VARCHAR(255),

    KEY idx_zip_last_first (zipcode, lastname, firstname)
);

执行:

复制代码
SELECT *
FROM people
WHERE zipcode = '95054'
  AND lastname LIKE '%etrunia%'
  AND address LIKE '%Main Street%';

联合索引是:

复制代码
(zipcode, lastname, firstname)

没有 ICP 时,大致过程是:

复制代码
1. 使用索引找到 zipcode = '95054' 的索引记录
2. 对每一条索引记录回表,读取完整行
3. 回到 MySQL Server 层判断:
   lastname LIKE '%etrunia%'
   address LIKE '%Main Street%'
4. 不满足条件的行丢弃

过程可以表示为:

复制代码
索引记录 1 → 回表读完整行 → 判断条件 → 丢弃
索引记录 2 → 回表读完整行 → 判断条件 → 丢弃
索引记录 3 → 回表读完整行 → 判断条件 → 保留

问题在于:很多记录最终会被过滤掉,但已经发生了回表和完整行读取

二、有索引下推时

启用 ICP 后,过程变成:

复制代码
1. 使用索引找到 zipcode = '95054' 的索引记录
2. 直接从索引中判断 lastname LIKE '%etrunia%'
3. 不满足的索引记录直接跳过
4. 满足的记录才回表读取完整行
5. 回表后再判断 address LIKE '%Main Street%'

过程变成:

复制代码
索引记录 1 → 判断 lastname → 不满足 → 不回表
索引记录 2 → 判断 lastname → 不满足 → 不回表
索引记录 3 → 判断 lastname → 满足 → 回表 → 判断 address

所以 ICP 主要减少的是:

回表次数

完整数据行读取次数

存储引擎与 Server 层之间的数据传递

相关磁盘 I/O

三、为什么 lastname 可以被下推?

因为 lastname 在联合索引中:

复制代码
(zipcode, lastname, firstname)

虽然查询条件:

复制代码
lastname LIKE '%etrunia%'

因为以 % 开头,通常不能用来直接定位 B+ 树的起始范围,但索引记录中确实包含 lastname,所以可以:

复制代码
先扫描 zipcode = '95054' 的索引记录
再在索引内部判断 lastname

这就是 ICP 的典型场景:

复制代码
某个条件不能帮助缩小索引扫描范围,
但可以帮助减少后续回表。

address 不在索引中:

复制代码
(zipcode, lastname, firstname)

因此必须回表读取完整行后,才能判断:

复制代码
address LIKE '%Main Street%'

四、 "索引条件"和"下推条件"的区别

还是以索引:

复制代码
(zipcode, lastname, firstname)

为例。

复制代码
WHERE zipcode = '95054'
  AND lastname LIKE '%etrunia%'
  AND address LIKE '%Main Street%'

大致可以这样理解:

条件 作用
zipcode = '95054' 用于定位和扫描索引范围
lastname LIKE '%etrunia%' 可以在索引中判断,适合索引下推
address LIKE '%Main Street%' 不在索引中,只能回表后判断

也就是说:

复制代码
索引访问条件:
决定"扫描哪些索引记录"

索引下推条件:
决定"哪些索引记录值得回表"

两者不是完全一回事

五、ICP 和覆盖索引的区别

覆盖索引

如果查询需要的字段都在索引中:

复制代码
CREATE INDEX idx_zip_last_first
ON people(zipcode, lastname, firstname);

SELECT zipcode, lastname, firstname
FROM people
WHERE zipcode = '95054'
  AND lastname LIKE '%etrunia%';

数据库可以直接从索引返回结果,不需要回表,这叫覆盖索引

复制代码
索引 → 直接返回

索引下推

如果查询还需要索引之外的列:

复制代码
SELECT *
FROM people
WHERE zipcode = '95054'
  AND lastname LIKE '%etrunia%'
  AND address LIKE '%Main Street%';

则仍然需要回表,但 ICP 会尽量减少回表数量:

复制代码
索引过滤 → 满足条件的记录回表

可以简单记忆:

复制代码
覆盖索引:完全不回表
索引下推:尽量少回表

六、 什么情况下收益明显?

ICP 的收益通常在以下场景比较明显:

使用的是 InnoDB 二级索引

索引扫描出来的候选记录较多

额外过滤条件的选择性较高

完整行比较宽,读取成本较高

回表需要较多随机 I/O

例如

复制代码
索引扫描 100 万条
最终只有 1000 条符合 lastname 条件

没有 ICP:

复制代码
可能需要回表 100 万次

有 ICP:

复制代码
先在索引中筛选,可能只回表约 1000 次

实际次数由执行计划和数据分布决定,但优化方向就是减少无效回表

七、 什么情况下不能使用?

ICP 不是所有条件都能下推。常见限制包括

条件使用了不在索引中的列

条件包含子查询

条件调用存储函数

某些触发条件无法下推查询不需要读取完整行时,ICP 本身没有太大意义

InnoDB 聚簇索引通常不使用 ICP,因为读取聚簇索引记录时完整行已经被读入 Buffer Pool

对于 InnoDB,ICP 主要针对二级索引 ;官方文档也说明,它适用于 rangerefeq_refref_or_null 等访问方式,并且前提是查询需要读取完整表行

相关推荐
风123456789~2 小时前
【Oracle专栏】ORA-02069: global_names 参数
数据库·oracle
ningmengjing_2 小时前
Redis 从入门到实战:Python操作全攻略
数据库·redis·python
隔窗听雨眠3 小时前
OceanBase旁路导入与自增主键冲突深度解析:原理、排查与解决方案
java·服务器·数据库
ZCBUS实时计算3 小时前
政务海量分库分表汇聚实战:基于 3 节点 ZCBUS 集群完成医保 10TB 数据实时整合
大数据·数据库·数据仓库·sql·dba·etl·政务
名不经传的养虾人3 小时前
从0到1:企业级AI项目迭代日记 Vol.81|工具调用前,先过一道审批门
数据库·人工智能·ai编程·ai工作流·企业ai
Databend3 小时前
Databend 产品更新:从 Spatial Index Join 到 Eval 数据管道
大数据·数据库·sql
数据知道4 小时前
SQL 注入从入门到精通:手工注入 + sqlmap 自动化
网络·数据库·sql·网络安全·自动化
lilian2334 小时前
Harmony os 技术实战|拼豆制图06:收藏 ID、生成记录与重启恢复怎么不打架
android·java·数据库·harmonyos
2601_955759624 小时前
ClaudeAPI成本中心与业务标签设计指南
android·java·数据库