① 什么是ICP
Index Condition Pushdown(ICP)是MySQL5.6中新特性,是一种在存储引擎层使用索引过滤数据的优化方式。
MySQL的执行计划(Explain)中,我们有时会在Extra列中看到 Using index condition。其就是表示索引条件下推(ICP)。
如果没有ICP,存储引擎会遍历索引以定位基表中的行,并将它们返回给MySQL服务器,由MySQL服务器评估where后面的条件是否保留行。
启用ICP后,如果部分 where
条件可以仅使用索引中的列进行筛选,则MySQL服务器会把这部分 where
条件放到存储引擎筛选。然后,存储引擎通过使用索引条目来筛选数据,并且只有在满足这一条件时才从表中读取行。
- 好处:ICP可以减少存储引擎必须访问基表的次数和MySQL服务器必须访问存储引擎的次数。
- 但是,ICP的
加速效果
取决于在存储引擎内通过ICP筛选
掉的数据的比例。
② ICP的开启和关闭
默认情况下启用索引条件下推。可以通过设置系统变量 optimizer_switch
来控制index_condition_pushdown
:
index_merge=on,index_merge_union=on,
index_merge_sort_union=on,index_merge_intersection=on,
engine_condition_pushdown=on,
index_condition_pushdown=on,
mrr=on,mrr_cost_based=on,block_nested_loop=on,
batched_key_access=off,materialization=on,
semijoin=on,loosescan=on,firstmatch=on,
duplicateweedout=on,subquery_materialization_cost_based=on,
use_index_extensions=on,
condition_fanout_filter=on,derived_merge=on
开启和关闭:
# 开启
set optimizer_switch='index_condition_pushdown=on';
# 关闭
set optimizer_switch='index_condition_pushdown=off';
③ ICP实例
如下所示,表people中有联合索引KEY
zip_last_first (
zipcode,
lastname,
firstname)
,当我们执行如下SQL时,就会触发ICP。
EXPLAIN SELECT * FROM people
WHERE zipcode='000001'
AND lastname LIKE '%张%'
AND address LIKE '%北京市%';
执行查看SQL的查询计划,Extra中显示了Using index condition ,这表示使用了索引下推。另外,Using where表示条件中包含需要过滤的非索引列的数据,即 address like ‘%北京市%’
这个标记并不是索引列,需要在服务端过滤掉。
这个如何理解呢?
按照我们的认知,其首先使用索引zipcode='000001'
查找,然后回表到主键索引(聚簇索引)中进行查找。假设有1000条,那么我们需要回表1000次并将数据读取到内存中再进行 lastname LIKE '%张%'
和 address LIKE '%北京市%'
的过滤。毕竟 lastname LIKE '%张%'
按照我们前面的学习,是用不到索引的。
如果MySQL服务器用了ICP,联合索引中包含了lastname,那么就可以在二级索引查找的时候就同时进行zipcode='000001 AND lastname LIKE '%张%'
的过滤,假设这时得到100条数据。那么就只需要回表100次,再进行address LIKE '%北京市%'
的过滤。
也就是ICP减少了回表次数,也同时减少了回表读取数据导致的随机IO次数。
那么对于可以正常使用联合索引的SQL呢?如下所示
EXPLAIN SELECT * FROM people
WHERE zipcode='000001'
AND lastname LIKE '张%'
AND firstname LIKE '三%';
按照我们的理解,是可以正常使用到索引的。但是查看执行计划,其Extra仍然存在Using index condition。不过查看此时的key_len能够发现确实是用到了三个字段索引。
可以自己做一下测试,数据量比较大的时候分别在两种情况下(是否使用ICP)验证查询性能,如下图所示没有ICP的查询明显要慢一些。
④ ICP的使用条件
- 如果表访问的类型为range、ref、eq_ref 和 ref_or_null 可以使用ICP
- ICP可以用于InnoDB 和 MyISAM表,包括分区表InnoDB和MyISAM表
- 对于InnoDB表,ICP仅用于二级索引。ICP的目标是减少全行读取次数,从而减少IO操作。
- 当SQL使用覆盖索引时,不支持ICP。因为这种情况下使用ICP不会减少IO。
- 相关子查询的条件不能使用ICP。