using index condition 表示 mysql 启用了 icp 优化,将部分 where 条件下推至存储引擎层(如 innodb)在索引遍历中提前过滤,减少回表或 server 层判断开销,仅对二级索引生效且要求条件可由索引字段独立评估。

Using index condition 是 MySQL 用 ICP 优化 WHERE 的信号
它表示 MySQL 下推了部分 WHERE 条件到存储引擎层,在索引遍历过程中提前过滤,而不是把所有索引记录回表或读全再在 Server 层判断。
典型场景是:联合索引前导列能走范围(比如 a = 1 AND b > 5 AND c = 10),但 c 不在范围列之后连续命中,Server 层无法用索引直接定位 c,于是把 c = 10 下推给 InnoDB,在读取每条满足 a=1, b>5 的索引项时,由引擎自己检查 c 值是否匹配——省去回表或读聚簇索引再判断的开销。
-
EXPLAIN中出现Using index condition,说明 ICP 已启用(前提是引擎支持,InnoDB 和 MyISAM 支持,Memory 不支持) - ICP 只对二级索引生效;对主键/聚簇索引无效,因为引擎层本就要读整行
- 下推的条件必须是“可被索引字段独立评估”的表达式,比如
c = 10、c IN (1,2,3),但UPPER(c) = 'ABC'或c + 1 > 5就不会下推
为什么有时候 EXPLAIN 显示 Using index condition,但实际没加速?
ICP 不是万能加速器。它只减少从引擎返回给 Server 层的数据量,不减少索引扫描行数本身。如果过滤条件选择性差(比如 status = 0 占 90% 行),下推后仍要处理大量索引项,且每次都要做一次字段解码和比较,反而可能略增 CPU 开销。
- 确认真实收益得看
Handler_read_next和Handler_icp_attempts状态变量(执行 SQL 后查SHOW STATUS LIKE 'Handler%'):前者下降明显,后者接近前者,说明 ICP 真正在干活 - 若
key_len很小(比如只用到联合索引第一列),但Extra有Using index condition,大概率是后续列的等值条件被下推了,但索引利用效率其实不高 - MySQL 5.6+ 默认开启
optimizer_switch='index_condition_pushdown=on',但某些复杂查询(含子查询、UNION、外连接)中 ICP 可能被自动禁用
如何验证某个条件是否真的被 ICP 下推?
最直接的办法是关掉 ICP 对比执行计划和性能:
SET optimizer_switch='index_condition_pushdown=off'; EXPLAIN SELECT * FROM t WHERE a = 1 AND b > 5 AND c = 10;
你会发现 Extra 变成 Using where,且 rows 预估可能变大(因为 Server 层要处理更多中间结果)。
- 用
SELECT ... INTO DUMPFILE或慢日志中的Rows_examined对比开关 ICP 前后的实际扫描行数差异 - 注意:仅靠
EXPLAIN的rows不可靠,它是预估,不是真实下推效果;真实下推效果看Handler_icp_match/Handler_icp_attempts比值(越接近 1,下推越有效) - 如果关掉 ICP 后性能几乎不变,说明那个条件本来选择性就极低,或者索引结构已让它天然高效过滤(比如
c是联合索引最后一列且是等值)
ICP 和覆盖索引(Using index)能同时出现吗?
可以,而且很常见。比如 SELECT c FROM t WHERE a = 1 AND b > 5 AND c = 10,如果 (a,b,c) 是联合索引,那么:
-
Using index表示只访问索引就拿到全部所需字段(覆盖索引) -
Using index condition表示c = 10这个条件被下推到引擎层判断 - 两者不冲突:引擎先按
a=1, b>5扫描索引项,在每项上检查c是否等于 10,若是,直接取出c值返回——全程不回表、不下推失败也不额外读数据 - 但如果
SELECT *,而索引不包含所有字段,就会同时出现Using index condition(下推过滤)和Using where(Server 层补过滤)甚至Using filesort,说明 ICP 只解决了一部分问题
真正容易被忽略的是:ICP 的触发依赖于优化器对索引统计信息的判断。如果 ANALYZE TABLE 没更新,或者数据倾斜严重(比如某 c 值占比异常高),优化器可能误判下推收益,导致该下推却没下推,或者下了推但没生效。这时候看执行计划只是第一步,还得盯住运行时状态变量。











