icp能减少磁盘io,因为其将索引列的过滤条件下推至存储引擎层,在二级索引扫描时直接利用索引中已有的字段值(如b=20)提前筛除不满足条件的记录,避免提取主键、触发回表及后续聚簇索引页的随机磁盘读取,实测可减少70%–95%无效回表。

ICP 为什么能减少磁盘 IO?
因为 ICP 把原本必须回表才能判断的条件,提前在二级索引扫描阶段就筛掉了——那些压根不满足条件的索引项,连主键都不取,更不会触发后续的聚簇索引页读取。
回表操作本身就要读磁盘,ICP 直接砍掉大量无效回表
InnoDB 的二级索引叶子节点只存(索引列值 + 主键),不存整行数据。要查 SELECT * 或非索引列,就必须用主键去聚簇索引里捞完整行——这个“回表”就是一次随机磁盘 IO(除非页已在 buffer pool)。
没有 ICP 时,只要索引匹配前缀(比如 idx_ab(a,b) 上查 a > 10),引擎就把所有对应主键全交给 Server 层;Server 层再逐个回表、取整行、判断 b = 20 是否成立。中间大量 b ≠ 20 的记录,白白消耗了磁盘寻道和页加载。
启用 ICP 后,引擎在遍历 idx_ab 叶子节点时,直接拿节点里已有的 b 值做判断:b = 20 不成立?跳过。连主键都不提取,自然跳过后续所有回表动作。
- 每跳过 1 条索引记录,就省下 1 次聚簇索引页的随机读
- 实际业务中,常有 70%–95% 的索引匹配项被 ICP 在引擎层过滤掉(尤其范围查询 + 等值筛选组合)
- IO 减少不是线性的——磁盘寻道成本高,哪怕少读 10 页,响应时间也可能降一半
哪些条件能被下推?不是所有 WHERE 都行
ICP 只下推「能在索引结构里直接拿到值」的条件。关键限制有:
- 必须是二级索引(聚簇索引叶子即数据,无需回表,ICP 无意义)
- 条件字段必须全部包含在该二级索引中(例如
idx_name_age(name,age)支持name='Li' AND age>25,但不支持city='BJ') - 不支持函数、子查询、存储过程调用(如
UPPER(name)='LI'或id IN (SELECT ...)) - 覆盖索引查询(
Using index)也不会触发 ICP——因为根本不需要回表
执行计划里看到 Extra: Using index condition 才算真正生效;如果只有 Using where,说明过滤仍在 Server 层。
MySQL 5.6+ 默认开,但某些场景会被悄悄禁用
optimizer_switch 控制全局开关,默认是 index_condition_pushdown=on。但以下情况 ICP 实际不工作:
- 查询用了
SELECT *但索引不覆盖所有列 → ICP 仍可用(只要条件列在索引里) - 使用分区表且 MySQL 版本
- 虚拟生成列上的辅助索引 → 不支持 ICP
- 事务隔离级别为
SERIALIZABLE且开启innodb_lock_wait_timeout等特殊锁行为时,优化器可能主动退避
最稳妥的验证方式:始终用 EXPLAIN FORMAT=TRADITIONAL 看 Extra 字段,别只信版本号或默认配置。











