MySQL 索引下推(ICP)什么时候生效,什么时候反而更慢
索引下推(Index Condition Pushdown,ICP)是 MySQL 5.6 引入的优化,把本该在 Server 层做的条件过滤下推到存储引擎层,减少回表次数。但它不是万能开关,有些场景打开反而更慢。
先说原理。没有 ICP 时,存储引擎按索引找到主键后全部回表,把整行交给 Server 层,再由 Server 层用 WHERE 里的条件过滤。有了 ICP,存储引擎在索引上就能用索引列做条件判断,不满足的直接跳过,不回表。所以 ICP 的收益等于「被跳过的回表率 × 单次回表成本」。
生效条件有四个,缺一不可。第一,必须是范围扫描、ref、eq_ref 或 ref_or_null 类型的访问,全表扫描用不上。第二,条件必须只涉及索引里的列,如果用到了索引外的列,存储引擎无从判断。第三,必须是 InnoDB 或 MyISAM,且优化器开关 index_condition_pushdown 是开的。第四,WHERE 条件在索引上可评估,也就是函数包裹索引列这种写法不算。
看个例子。联合索引 (a, b),查询 where a = 1 and b like '%xx%'。a 能用于索引定位,b 因为前缀模糊无法定位,但 b 在索引里,可以下推判断,只有 b 命中的行才回表。这时候 ICP 收益最大。
反过来,什么时候它反而慢。第一种,索引列的选择性极差。比如性别这种列,下推判断后仍然命中一半的行,等于白算一遍,还多了一次引擎层判断开销。第二种,索引覆盖了大部分列,回表成本本来就低,ICP 的收益抵不过判断开销。第三种,联合索引里下推的列在最右侧且数据分布倾斜,索引扫描本身就要扫很多行,ICP 只是减少了回表,扫描量没变。
怎么验证。EXPLAIN 输出里的 Extra 列出现 Using index condition,说明 ICP 生效了。对比开关前后的执行计划:
set optimizer_switch='index_condition_pushdown=off';
explain select ...;
set optimizer_switch='index_condition_pushdown=on';
explain select ...;
再用 handler_read_next 和 handler_read_rnd_next 的差值看实际回表次数,比执行时间更可靠,因为执行时间受缓存影响大。
一个常见误判:把 ICP 和覆盖索引(Using index)搞混。覆盖索引是不回表,ICP 是少回表,两者可以同时出现,Extra 里会写 Using index condition; Using index。看到只有 Using index 才是彻底不回表。
最后提醒,联合索引的列顺序决定了 ICP 能否发挥作用。把常用于范围过滤的列放在联合索引的靠前位置,让它既能用于定位又能用于下推,收益最高。
- 点赞
- 收藏
- 关注作者
评论(0)