一、SQL的where条件提取规则

在ICP(Index Condition Pushdown,索引条件下推)特性之前,必须先搞明白根据何登成大神总结出一套放置于所有SQL语句而皆准的where查询条件的提取规则:所有SQL的where条件,均可归纳为3大类:Index Key (First Key & Last Key),Index Filter,Table Filter。

接下来,简单说一下这3大类分别是如何定义,以及如何提取的,详情请看:SQL语句中where条件在数据库中提取与应用浅析

Index Key(Fist key & Last Key),Index Filter,Table Filter

Index First Key

只是用来定位索引的起始范围,因此只在索引第一次Search Path(沿着索引B+树的根节点一直遍历,到索引正确的叶节点位置)时使用,一次判断即可;

Index Last Key

用来定位索引的终止范围,因此对于起始范围之后读到的每一条索引记录,均需要判断是否已经超过了Index Last Key的范围,若超过,则当前查询结束;

Index Filter

用于过滤索引查询范围中不满足查询条件的记录,因此对于索引范围中的每一条记录,均需要与Index Filter进行对比,若不满足Index Filter则直接丢弃,继续读取索引下一条记录;

Table Filter

则是最后一道where条件的防线,用于过滤通过前面索引的层层考验的记录,此时的记录已经满足了Index First Key与Index Last Key构成的范围,并且满足Index Filter的条件,回表读取了完整的记录,判断完整记录是否满足Table Filter中的查询条件,同样的,若不满足,跳过当前记录,继续读取索引的下一条记录,若满足,则返回记录,此记录满足了where的所有条件,可以返回给前端用户。

二、ICP特性介绍

Index Condition Pushdown (ICP)是MySQL 5.6版本中的新特性,是一种在存储引擎层使用索引过滤数据的一种优化方式。

我对Using index condition的理解是,首先mysql server和storage engine是两个组件,server负责sql的parse,执行; storage engine去真正的做数据/index的读取/写入。以前是这样:server命令storage engine按index key把相应的数据从数据表读出,传给server,然后server来按where条件(index filter和table filter)做选择。

而在MySQL 5.6加入ICP后,Index Filter与Table Filter分离,Index Filter下降到InnoDB的索引层面进行过滤,如果不符合条件则无须读数据表,减少了回表与返回MySQL Server层的记录交互开销,节省了disk IO,提高了SQL的执行效率。

原理

a. 当关闭ICP时,index仅仅是data access的一种访问方式,存储引擎通过索引回表获取的数据会传递到MySQL Server层进行where条件过滤,也就是做index filter和table filter。

b. 当打开ICP时,如果部分where条件能使用索引中的字段,MySQL Server会把这部分下推到引擎层,可以利用index filter的where条件在存储引擎层进行数据过滤,而非将所有通过index access的结果传递到MySQL server层进行where过滤。

优化效果:ICP能减少引擎层访问基表的次数和MySQL Server访问存储引擎的次数,减少io次数,提高查询语句性能。

三、测试ICP优化SQL

本文选用MySQL官方文档中提供的示例数据库之一:employees。这个数据库关系复杂度适中,且数据量较大。下图是这个数据库的E-R关系图(引用自MySQL官方手册):

MySQL官方文档中关于此数据库的页面为:https://dev.mysql.com/doc/employee/en,里面详细介绍了此数据库,并提供了下载地址和导入方法,如果有兴趣导入此数据库到自己的MySQL可以参考文中内容。

可以选择下载测试数据:https://github.com/datacharmer/test_db

关闭缓存(最好关闭后重启MySQL)

mysql> set global query_cache_size=0;
mysql> set global query_cache_type=OFF;

导入employees库,需要自己手动创建一个联合索引。

mysql> alter table employees add index first_last(first_name,last_name);

其表结构如下:

mysql> show create table employees\G
*************************** 1. row ***************************
Table: employees
Create Table: CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` enum('M','F') NOT NULL,
`hire_date` date NOT NULL,
PRIMARY KEY (`emp_no`),
KEY `first_last` (`first_name`,`last_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)

当开启ICP时(默认开启)

mysql> SET profiling = 1;
Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> select SQL_NO_CACHE * from employees where first_name='Anneke' and last_name like '%Preusig' ;
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date |
+--------+------------+------------+-----------+--------+------------+
| 10006 | 1953-04-20 | Anneke | Preusig | F | 1989-06-02 |
+--------+------------+------------+-----------+--------+------------+
1 row in set (0.00 sec)

此时情况下根据MySQL的最左前缀原则,irst_name 可以使用索引,last_name采用了like 模糊查询,不能使用索引。

当关闭ICP时

mysql> set optimizer_switch='index_condition_pushdown=off';
Query OK, 0 rows affected (0.00 sec) mysql> select SQL_NO_CACHE * from employees where first_name='Anneke' and last_name like '%Preusig' ;
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date |
+--------+------------+------------+-----------+--------+------------+
| 10006 | 1953-04-20 | Anneke | Preusig | F | 1989-06-02 |
+--------+------------+------------+-----------+--------+------------+
1 row in set (0.00 sec) mysql> SET profiling = 0;
Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> show profiles;
+----------+------------+---------------------------------------------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+---------------------------------------------------------------------------------+
| 1 | 0.00108900 | select * from employees where first_name='Anneke' and last_name like '%Preusig' |
| 2 | 0.00025375 | set optimizer_switch='index_condition_pushdown=off' |
| 3 | 0.00231650 | select * from employees where first_name='Anneke' and last_name like '%Preusig' |
+----------+------------+---------------------------------------------------------------------------------+
5 rows in set, 1 warning (0.00 sec)

当开启ICP时,查询在sending data环节时间消耗是 0.00108900s

当关闭ICP时,查询在sending data环节时间消耗是 0.00231650s

从上面的profile可以看出ICP开启时整个sql 执行时间是未开启的2/3,sending data 环节的时间消耗前者仅是后者的1/4。

ICP 开启时的执行计划 含有 Using index condition 标示 ,表示优化器使用了ICP对数据访问进行优化。ICP关闭时的执行计划显示use where。
 
mysql> set optimizer_switch='index_condition_pushdown=on';
Query OK, 0 rows affected (0.00 sec) mysql> explain select * from employees where first_name='Anneke' and last_name like '%nta' ;
+----+-------------+-----------+------+---------------+------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------------+---------+-------+------+-----------------------+
| 1 | SIMPLE | employees | ref | first_last | first_last | 58 | const | 224 | Using index condition |
+----+-------------+-----------+------+---------------+------------+---------+-------+------+-----------------------+
1 row in set (0.00 sec) mysql> explain select * from employees where first_name='Anneke' ;
+----+-------------+-----------+------+---------------+------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------------+---------+-------+------+-----------------------+
| 1 | SIMPLE | employees | ref | first_last | first_last | 58 | const | 224 | Using index condition |
+----+-------------+-----------+------+---------------+------------+---------+-------+------+-----------------------+
1 row in set (0.00 sec) mysql> set optimizer_switch='index_condition_pushdown=off';
Query OK, 0 rows affected (0.00 sec) mysql> explain select * from employees where first_name='Anneke' and last_name like '%nta' ;
+----+-------------+-----------+------+---------------+------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------------+---------+-------+------+-------------+
| 1 | SIMPLE | employees | ref | first_last | first_last | 58 | const | 224 | Using where |
+----+-------------+-----------+------+---------------+------------+---------+-------+------+-------------+
1 row in set (0.00 sec)

四、案例分析

以上面的查询为例,关闭ICP时,存储引擎通前缀index first_name(视为index key)访问表中数据,并在MySQL server层根据where条件last_name like ‘%nta’(视为index filter)进行过滤。

开启ICP时,MySQL server把index filter(last_name like ‘%nta’)推到存储引擎层,在存储引擎内部通过与where条件last_name like ‘%nta’的对比,直接过滤掉不符合条件的数据,然后返回最终数据给MySQL server层。该过程减少了回表操作,只访问符合条件的1条记录并返回给MySQL Server ,有效的减少了io访问和各层之间的交互。

ICP关闭时 ,仅仅使用索引作为访问数据的方式。

ICP 开启时 ,MySQL将在存储引擎层 利用索引过滤数据,减少不必要的回表。

注意虚线的using where表示如果where条件中含有没有被索引的字段,则还是要经过MySQL Server 层过滤。

五、ICP的使用限制

1. 当sql需要全表访问时,ICP的优化策略可用于range, ref, eq_ref, ref_or_null类型的访问数据方法 。

2. 支持InnoDB和MyISAM表。

3. ICP只能用于二级索引,不能用于主索引。

4. 并非全部where条件都可以用ICP筛选,如果where条件的字段不在索引列中,还是要读取整表的记录到server端做where过滤。

5. ICP的加速效果取决于在存储引擎内通过ICP筛选掉的数据的比例。

6. MySQL 5.6版本的不支持分表的ICP功能,5.7版本的开始支持。

7. 当sql使用覆盖索引时,不支持ICP优化方法。

 

原文链接:http://blog.itpub.net/22664653/viewspace-1210844/

MySQL ICP(Index Condition Pushdown)特性的更多相关文章

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

    一.ICP优化原理 Index Condition Pushdown (ICP),也称为索引条件下推,体现在执行计划的上是会出现Using index condition(Extra列,当然Extra ...

  2. MySQL 之 Index Condition Pushdown(ICP)

    简介 Index Condition Pushdown (ICP)是MySQL 5.6 版本中的新特性,是一种在存储引擎层使用索引过滤数据的一种优化方式. 当关闭ICP时,index 仅仅是data ...

  3. MySQL 优化之 ICP (index condition pushdown:索引条件下推)

    ICP技术是在MySQL5.6中引入的一种索引优化技术.它能减少在使用 二级索引 过滤where条件时的回表次数 和 减少MySQL server层和引擎层的交互次数.在索引组织表中,使用二级索引进行 ...

  4. 【mysql】关于Index Condition Pushdown特性

    ICP简介 Index Condition Pushdown (ICP) is an optimization for the case where MySQL retrieves rows from ...

  5. MySQL中Index Condition Pushdown(ICP)优化

    在MySQL 5.6开始支持的一种根据索引进行查询的优化方式.之前的MySQL数据库版本不支持ICP,当进行索引查询是,首先根据索引来查找记录,然后在根据WHERE条件来过滤记录.在支持ICP后,My ...

  6. mysql -- 优化之ICP(index condition pushdown)

    一.为了方法说明ICP是什么.假设有如下的表和查询: create table person( id int unsigned auto_increment primary key, home_add ...

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

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

  8. MySQL 5.6新特性 -- Index Condition Pushdown

    Index Condition Pushdown(ICP)是针对mysql使用索引从表中检索行数据时的一种优化方法.   在没有ICP特性之前,存储引擎根据索引去基表查找并将数据返回给mysql se ...

  9. 1229【MySQL】性能优化之 Index Condition Pushdown

    转自http://blog.itpub.net/22664653/viewspace-1210844/  [MySQL]性能优化之 Index Condition Pushdown2014-07-06 ...

随机推荐

  1. opencv学习之路(6)、鼠标截图,滑动条播放视频

    一.鼠标截图 #include<opencv2/opencv.hpp> #include<iostream> using namespace cv; using namespa ...

  2. java基础篇之理解synchronized的用法

    Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问同一个对象object中的这个synchronized(this ...

  3. Spring 学习——Spring JSR注解——@Resoure、@PostConstruct、@PreDestroy、@Inject、@Named

    JSR 定义:JSR是Java Specification Requests的缩写,意思是Java 规范提案.是指向JCP(Java Community Process)提出新增一个标准化技术规范的正 ...

  4. ODAC(V9.5.15) 学习笔记(四)TCustomDADataSet(2)

    2.连接相关 名称 类型 说明 Connection 指向一个数据库连接对象 Disconnected 设置为True将在数据库关闭后继续保持数据集的开启状态. 3. 数据获取 名称 类型 说明 Fe ...

  5. Golang中使用kafka

    golang中比较好用的kafka client有 sarama confluent-kafka-go go_kafka_client optiopay-kafka siesta 其中 sarama的 ...

  6. mysql数据库连接的测试代码语句片断

    手动连接数据库 的代码片断, 用于各种数据库的测试... $conn = mysql_connect('localhost', 'root', '') or die('failed to connec ...

  7. dh

    -.-- -.. --- -. --- - -.- -. --- .--

  8. How can I move a MySQL database from one server to another?

    My favorite way is to pipe a sqldump command to a sql command. You can do all databases or a specifi ...

  9. R dataframe 列名包含减号,列名自动变为点

    d<- data.frame(x = c(0, 1)) d<- data.frame(d, y = c(0,1)) names(d)[2]<- "a.-5" d ...

  10. ORM框架 之 Entity Framework

    Entity Framework 1.ADO.NET Entity Framework是以ADO.NET为基础所发展出来的对象关系对应(O/R Mapping)解决方案,早起被称为ObjectSpac ...