**索引下推(ICP,Index Condition Pushdown)**是 MySQL 5.6+ 默认开启的优化:把 WHERE 里能在索引上判断的条件,下推到存储引擎层,在遍历索引时就过滤掉不满足条件的行,从而大幅减少回表次数和 I/O。
下面用最清晰、面试可直接背的方式讲。
一、一句话理解
- 没有 ICP :只按索引最左前缀过滤 → 所有匹配前缀的都要回表 → Server 层再过滤 → 大量无效回表。
- 有 ICP :索引里有的字段条件,在索引阶段就过滤 → 只回表那些真正符合全部条件的 → 回表变少、性能变快。
图书馆类比(好记):
- 索引=目录(姓名、年龄、书架号)
- 查:姓名=张三 AND 年龄=20
- 无 ICP:把所有"张三"的目录都拿出来,逐个去书架拿书,再看年龄是不是20。
- 有 ICP:直接在目录里筛出"张三+年龄20",只拿这几本去书架。
二、原理与流程(必背)
假设有联合索引 idx(a,b,c),SQL:
sql
select * from t where a=1 and b>2 and c like 'x%';
1)无 ICP(MySQL 5.6 前)
- Server 层:给存储引擎条件
a=1。 - 存储引擎:扫描索引,找出所有
a=1的索引项 → 全部回表拿整行。 - Server 层:再过滤
b>2 and c like 'x%'→ 很多白回表。
2)有 ICP(默认开启)
- Server 层:把
a=1 and b>2 and c like 'x%'一起下推给存储引擎。 - 存储引擎:遍历索引时,直接在索引上判断 a、b、c。
- 只有全部条件满足的索引项,才去回表拿整行。
- 无效行在索引阶段就丢掉,不回表、不浪费 I/O。
执行计划标志
EXPLAIN 中 Extra 显示:Using index condition → 正在使用 ICP。
三、生效条件(面试常问)
- MySQL 5.6+ ,默认开启(
optimizer_switch='index_condition_pushdown=on')。 - 引擎:InnoDB / MyISAM(不支持全文索引)。
- 索引:联合索引(最左前缀匹配)。
- WHERE 条件:部分字段在索引里,且不是主键、不是函数/表达式。
- 访问类型:
range / ref / eq_ref / ref_or_null。
四、ICP vs 覆盖索引(区别必背)
- 覆盖索引(Using index) :查询列全在索引里 → 完全不回表,直接从索引拿数据。
- 索引下推(Using index condition) :必须回表 (如
select *),但尽量少回表。
一句话:
- 覆盖索引 = 不回表
- 索引下推 = 少回表
五、优化示例(直接能用)
表:user(name, age, city),联合索引 idx_name_age_city(name,age,city)
慢 SQL(无 ICP 效果)
sql
select * from user where name='张三' and city='北京';
- 索引最左前缀:
name='张三' - 无 ICP:所有
name=张三都回表 → Server 过滤city='北京' - 有 ICP:索引里有
city,直接在索引过滤 → 回表大幅减少。
开启/关闭 ICP
sql
-- 开启(默认)
set optimizer_switch='index_condition_pushdown=on';
-- 关闭(测试用)
set optimizer_switch='index_condition_pushdown=off';
六、总结(面试直接背)
- ICP 作用:减少回表次数,降低磁盘 I/O,提升查询性能。
- 核心原理:将索引字段的过滤条件下推到存储引擎,在索引遍历阶段过滤。
- 生效场景:联合索引、条件字段在索引中、InnoDB/MyISAM。
- 与覆盖索引区别:覆盖索引不回表,ICP 减少回表。