浅析MySQL中的Index Condition Pushdown (ICP 索引条件下推)和Multi-Range Read(MRR 索引多范围查找)查询优化

本文深入探讨了MySQL中的ICP(IndexConditionPushdown)和MRR(Multi-RangeRead)两种查询优化策略,解析了它们如何提升索引查找和二级索引范围查询的效率,以及在哪些场景下应用最为有效。

本文出处:http://www.cnblogs.com/wy123/p/7374078.html
(保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错误进行修正或补充,无他)

 

 

ICP优化原理

Index Condition Pushdown (ICP),也称为索引条件下推,体现在执行计划的上是会出现Using index condition(Extra列,当然Extra列的信息太多了,只能做简单分析)
ICP原理通俗讲就是,查询过程中,直接在查询引擎层的API获取数据的时候实现"非直接索引"过滤条件的筛选,而不是查询引擎层查询出来之后在Server层筛选。
换句话说就是ICP在获取数据的同时实现了where的次选条件中无法直接使用索引的情况下的筛选,避免了没有ICP优化的时候分两个步骤的实现(获取数据的过程没有做次选条件的过滤)
如果是非ICP优化查询的话,是两步,第一步是获取数据,第二步是获取的数据进行条件筛选。
显然,相比后者,前者可以一步实现索引的查找Seek+filter,效率上更高。

适应的场景:
ICP的优化策略可用于range、ref、eq_ref、ref_or_null 类型的访问数据方法

其实没有实例不太好理解这种优化策略,还是举两个实际列子吧。

 

ICP优化实例

第一个例子在网上非常多,也非常容易理解.具体表结构见上文(http://www.cnblogs.com/wy123/p/7366486.html

下面用到的test_orderdetail表的索引为:create index idx_orderid_productname on test_orderdetail(order_id,product_name);
查询语句为:select * from test_orderdetail where order_id = 10900 and product_name like '%00163e0496af%';
显然,order_id = 10900是可以直接进行索引查找的,虽然product_name也包含在复合索引中,但是product_name like '%00163e0496af%'是无法使用索引的
观察其执行计划,发现Extra中是Using index condition。

ICP在这里的优化原理就是,
在利用第一个条件 order_id = 10900 进行索引查找的过程中,同时使用product_name like '%00163e0496af%'这个无法直接使用索引查找的条件进行过滤。
最终一步就可以筛选出来结果。

set optimizer_switch='index_condition_pushdown=off或者on'

  对比关闭ICP优化的情况
  如果关闭ICP优化,执行计划的Extra显示为Using where,
  意味着使用order_id = 10900进行索引查找之后,再对结果集进行product_name like '%00163e0496af%'的筛选

  

  第二个例子是后面自己想的,为了验证ICP的出现场景,以及确实优于非ICP优化的情况

这一次使用的表是test_order,test_order上的索引为create index idx_userid_order_id_createdate on test_order(user_id,order_id,create_date);
查询语句为:select * from test_order where user_id = 500 and create_date > '2015-1-1';
与上面的例子一样,第二个筛选条件是无法直接使用索引的

     首先看两者的执行计划在ICP优化上的区别

  关闭ICP之后的执行计划

  然后分别执在打开与关闭ICP的情况下,观察其执行过程中的profile信息

  查看两个sql执行的详细信息,也即分别在打开与关闭ICP优化的情况下,如下,在stage/sql/Sending data环节有超过一个数量级的差异。
  也就意味着通过ICP机制的优化,server 层和 engine 层之间数据交互的次数减少。

  

  引用MySQL · 特性分析 · Index Condition Pushdown (ICP)中的一句话
  在二级索引是复合索引且前面的条件过滤性较低的情况下,打开 ICP 可以有效的降低 server 层和 engine 层之间交互的次数,从而有效的降低在运行时间。

 

  最后,再思考一个问题,
  对于select * from test_orderdetail where order_id = 10900 and product_name like '%00163e0496af%';这个查询,
  如果order_id 包含在一个二级索引中,但是product_name 没有包含在这个二级索引中,MySQL会不会采用ICP的方式进行优化?
  答案是否定的。
  因为ICP的前提两个查询条件包被索引覆盖,但是次选条件无法直接使用索引查找,如果次选条件没有被索引覆盖,是无法得知次选条件的值的,也就无从 索引条件下推优化了。

  

 

  

 

Multi-Range Read(MRR)

非MRR优化下存在的问题:
首先了解一点背景知识:MySQL的Innodb表都是聚集索引表,没有显式指定聚集索引的情况下,会自动生成一个聚集索引。
在使用二级索引(或者说是非聚集索引)进行范围查询的条件下,二级索引会根据其B树结构的叶子节点存储的聚集索引进行数据的查找(回表操作),
但是符合条件的数据(二级索引超找的数据)有可能是随机分布在聚集索引B树的任何一个部分,这样就可能存在表上过多的随机IO。
当表非常大的时候,每一行的查找过程都需要在磁盘上随机进行,可能会对性能造成影响。

举个例子,
如下图,参考蓝线的移动轨迹,二级索引查找到的目标数据行的物理位置为1,2,3,4(主要的是以何种顺序去获取这四个位置的数据,可以随机的方式获取,也可以顺序的方式获取,讲究就在这一点)
在查找这四个位置的数据的时候,如果直接按照二级索引对应的聚集索引的顺序查找,
由于二级索引排序的情况下,其对应的聚集索引的顺序可能是随机的,那么其对应的数据的物理位置也就是随机的了
如果按照二级索引的顺去回表超找对应的数据行,那么这个过程就需要随机IO查找。
这种查询方式的缺点,可以理解为在查询这四行数据的过程中,在物理位置差异较大的情况下,需要磁头来回摆臂来实现(随机IO读取)。

MRR多范围读取优化的目的是通过对记录的读取请求进行排序,然后再读取数据行的时候以顺序IO的方式进行,避免随机IO
究竟是对哪个字段排序?个人认为可以理解成二级索引范围查找到的对应的聚集索引的key值进行排序。
有序扫描的过程可以认为是:

(1)通过非聚集索引找到目标数据的聚集索引的key值
(2)对通过二级索引找到的目标数据的聚集索引的key值排序,此时聚集索引与物理位置一一对应。
(3)(回表的过程)通过二级索引对应的有序的聚集索引,执行一个有序的磁盘扫描来获取数据,从而来加快读取数据的速度。

顺序读磁盘通常会更快,当然也不是说这种方式的效率总是较高的,凡事有利必有弊,也有例外的情况

1,如果扫描的是一个较小的数据范围,并且目标数据已经在磁盘的缓存当中,MRR的唯一影响是为了缓冲/排序额外的增加了一些CPU开销。
2,order by *** LIMIT n查询,当n值比较小的时候,可能会变的更慢,
   原因是 MRR试图通过顺序读盘的方式(来或取数据),可能一开始读取到的数据并非总是排在(order by ***)符合前N条的。
3,MRR是一个实现过程,个人理解,极端情况下,如果MySQL不知道目标数据的行数,
   如果仅仅只有一行,依然要进行排序操作,然后回表读取数据行,这种情况下也是得不偿失的。

  打开MRR优化
  set global optimizer_switch = 'mrr=on,mrr_cost_based=off';

  启用MRR优化的前提是要进行书签超找,也即要回表,如果不需要回表的话,二级索引本身就可以查询出来需要的字段了,没有随机IO的机会的所谓了。

  如下截图,如果去掉order_status,也就意味着无需回表查询,那么就不会出现MRR优化了。

  同时,一旦出现MRR优化,查询出来的结果的顺序,必然是按照聚集索引来排序的,这个原理应该是不难理解的。

  

  当然MRR优化也有在表关联情况下的优化措施,原理大同小异。

总结:

    Index Condition Pushdown(索引条件下推)和Multi-Range Read(多范围读)都是MySQL为了提高查询优化而备用的选项,属于MySQL5.6里面的新特性。
    无奈楼主接触MySQL不久,见识不够,很是觉得新鲜,高手勿喷。
    两者的共同的特点都是在使用索引超找(或者索引范围扫描)的过程中的一些优化措施。
    这些优化措施可以在二级索引查找(索引范围扫描)的过程中优化查询动作的行为,
    当然这些优化措施并非总是万能的,允许用户显式打开或者关闭,给用户充分的自由,然而自由也并非完全没有问题,这也要求用户在做相关优化的时候需要进行充分的权衡和考虑。

 

参考:

    https://mariadb.com/kb/en/mariadb/multi-range-read-optimization/
    http://blog.itpub.net/22664653/viewspace-1673682/
    http://blog.itpub.net/22664653/viewspace-1678779/
       http://mysql.taobao.org/monthly/2015/12/08/

     以及各种网上搜索……

 

最后,mariadb官方这几张图非常赞,对理解问题很有帮助,先盗下来,备用(无耻一笑,O(∩_∩)O~),

突然又想到做人了,为什么一定要直来直去呢,很多时候是欲速则不达,迂回一下,暂时停下来,好好计划计划再出发,未必是坏事。

 

<think>好的,我现在需要帮助用户检查MySQL是否启用了索引条件下推Index Condition PushdownICP)的功能。首先,我得确认自己对ICP的理解是否正确。ICPMySQL 5.6引入的一个优化功能,它允许在存储引擎层应用WHERE子句中的某些条件,从而减少需要返回到服务器层的数据行数,提升查询性能。特别是当部分条件可以利用索引但无法完全过滤时,ICP能有效减少回表次数。 接下来,用户的问题是如何检查ICP是否启用。根据之前的知识,MySQL的系统变量通常可以通过SHOW VARIABLES或查询performance_schema来获取。我需要确定控制ICP的变量名称。记得ICP相关的变量是optimizer_switch中的一个选项,而optimizer_switch包含了优化器的开关,每个开关用逗号分隔。 因此,首先应该检查optimizer_switch变量的值,确认index_condition_pushdown是否启用。用户可能需要执行类似`SHOW VARIABLES LIKE 'optimizer_switch';`的命令,或者在会话级别动态修改这个变量。此外,也可以通过查询performance_schema的session_variables表来获取当前设置。 另外,考虑到用户可能不太熟悉如何解析optimizer_switch的值,我需要详细解释输出的格式,比如找到index_condition_pushdown=on这样的部分。同时,还要提醒用户,这个设置可以是全局的或会话级的,检查时需要注意当前会话的配置。 还需要考虑不同MySQL版本的差异。ICP在5.6及以后版本才支持,如果用户使用的是旧版本,可能不支持该功能。因此,可能需要建议用户先确认版本信息,使用`SELECT VERSION();`来查看。 此外,引用中提到的参考资料[^1][^2][^4][^5]都涉及到ICP的工作原理优化效果,可以引用这些内容来说明启用ICP的重要性,以及如何通过系统变量进行控制。例如,引用[^4]提到在没有ICP时,查询必须检索所有符合zipcode条件的行,而启用ICP后可以在索引层进行更过滤。 最后,用户可能需要临时启用或禁用ICP来进行测试,这时可以指导他们使用SET命令修改optimizer_switch,例如`SET SESSION optimizer_switch='index_condition_pushdown=on';`,如引用[^3]中提到的设置sort_buffer_size的方法类似。 总结步骤应该是:1. 检查MySQL版本是否支持ICP;2. 查看optimizer_switch中的index_condition_pushdown状态;3. 解释如何根据需要开启或关闭该功能。同时,提供示例命令输出解释,确保用户能够理解操作。</think>要检查MySQL是否启用了索引下推Index Condition Pushdown, ICP),可以通过以下步骤操作: --- ### **1. 查看`optimizer_switch`系统变量** ICP功能由`optimizer_switch`参数控制,需检查其包含的`index_condition_pushdown`选项状态。 执行以下SQL命令: ```sql SHOW VARIABLES LIKE 'optimizer_switch'; ``` 或更精确地提取相关值: ```sql SELECT @@optimizer_switch; ``` **输出示例**: ``` index_condition_pushdown=on, ..., mrr=on ``` - 若输出中包含`index_condition_pushdown=on`,表示ICP已启用[^2]。 --- ### **2. 通过`performance_schema`查询(可选)** 若需编程式获取状态,可查询`performance_schema`: ```sql SELECT VARIABLE_VALUE FROM performance_schema.session_variables WHERE VARIABLE_NAME = 'optimizer_switch'; ``` 解析返回的字符串中是否包含`index_condition_pushdown=on`。 --- ### **3. 临时启用/禁用ICP** 若需测试ICP效果,可在会话级别动态调整: - **启用ICP**: ```sql SET SESSION optimizer_switch = 'index_condition_pushdown=on'; ``` - **禁用ICP**: ```sql SET SESSION optimizer_switch = 'index_condition_pushdown=off'; ``` --- ### **4. 验证ICP效果** 通过执行计划(`EXPLAIN`)观察是否使用ICP: ```sql EXPLAIN SELECT * FROM table WHERE indexed_column = 'value' AND non_indexed_column = 'value'; ``` - 若`Extra`列显示`Using index condition`,则ICP生效。 --- ### **注意事项** - **版本要求**:ICP仅支持MySQL 5.6及以上版本,可使用`SELECT VERSION();`确认版本[^5]。 - **全局与会话设置**:若需全局生效,需在配置文件中修改`optimizer_switch`并重启服务。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值