你有没有遇到过这样的场景:查用户订单,条件是 status = 'shipped' 且 create_time > '2024-01-01',表有上千万行,但只加了 status 字段的单列索引——结果查询还是慢?明明走了索引,为啥还要回表过滤一堆数据?
索引下推,就是让“筛选动作”提前到索引扫描阶段
在 MySQL 5.6 之后,默认开启 Index Condition Pushdown(ICP),也就是常说的“索引下推”。它不改变索引结构,而是改变了执行流程:
以前:存储引擎按索引顺序读出主键(或聚簇索引记录),把整行数据返回给 Server 层,Server 层再判断 create_time > '2024-01-01' 是否成立。
现在(开启 ICP 后):只要索引里包含 create_time(比如联合索引 (status, create_time)),存储引擎在读索引项时就顺手把 create_time 的值拿出来比一比。不满足的记录,压根不取整行,直接跳过。
举个实际例子
假设有一张订单表:
CREATE TABLE `orders` (
`id` BIGINT PRIMARY KEY,
`user_id` INT,
`status` VARCHAR(20),
`create_time` DATETIME,
INDEX idx_status_ctime (`status`, `create_time`)
);
执行查询:
SELECT * FROM orders
WHERE status = 'shipped' AND create_time > '2024-03-01';
有 ICP 时,InnoDB 扫描 idx_status_ctime 索引,遇到 status='shipped' 但 create_time='2024-02-15' 的索引项,当场丢弃,不回表;没 ICP 时,得先把对应主键查出来、再回聚簇索引捞整行、最后 Server 层才过滤掉——多一次磁盘 IO,也多一次内存拷贝。
实测中,当过滤条件能筛掉 80% 以上索引项时,ICP 常常让查询快 2–3 倍,尤其对 SSD 延迟敏感的线上服务,效果更明显。
它不是万能的,但很实在
ICP 只作用于 二级索引,且要求索引中包含 WHERE 条件里的字段(至少部分)。像 ORDER BY、GROUP BY 或 HAVING 中的字段不参与下推;函数操作如 YEAR(create_time) = 2024 也会让 ICP 失效。不过日常写查询时,只要习惯建好覆盖度高的联合索引,ICP 就在默默帮你省资源。
就像快递分拣站——以前是把所有标着“上海”的包裹全拉到总仓,再挨个看门牌号;现在扫描面单时就顺手把“非浦东新区”的挑出去,剩下才是真正要送的。活儿没少干,但车少跑两趟,人也轻松点。