mysql5.6相比之前的5.5在对辅助索引查询的优化有了较大的改变,比如今天介绍的两款针对mysql辅助索引查询的multi range read(mrr)和index condition pushdown(icp)优化。这两种优化的目的就是对mysql在对辅助索引查询的提速。下面就对这两种优化的实现目的和他们解决的问题做一下基础性的介绍。
在对这两种优化做出概述之前,先来看下传统的mysql针对辅助索引查找数据实现的方法。这里主要介绍innodb存储引擎对于辅助索引的查找实现方式。我们知道,mysql在对辅助索引取数据的时候,先是通过索引页的叶子节点找到对应的主键id,再通过主键id找到对应的数据页,在数据页中最后通过二分查找找到对应的数据。这里我们设想一下,假如在一张表中对某个字段建立一个辅助索引,而这个字段有一些重复的数据,那么我们根据这个字段去做where条件的时候,势必每次取到的id不一定是顺序的,既然不会是顺序的,那么必然会产生一定的随机io。在计算机中,随机io的速度比顺序的io的速度慢很多,因为在一个柱面中,随机io必然会造成磁头的随机旋转,从而产生一定量的磁盘io,而顺序io则可降低到最低。面对这种情况,就是我们下面介绍的mrr优化大显身手的地方了。
上面已经说过,对非唯一的辅助索引查询,由于每次在辅佐索引页叶子节点上查找主键的id的时候不一定是顺序的,如果每次都通过查询出来的主键盘id去拿数据的话,就会产生我们上面所说的随机io的情况。mrr优化就在这里做了功夫,在通过辅佐索引页上拿到主键id后,并不是通过id直接去数据页中取数据。而是先通过排序算法,把取到的主键id按照从小到大的方式排序,然后再通过书签查找,取得对应的数据。这样就可以把随机io的情况降到最低。
上面说到的是mrr的一个好的方式,其实mrr优化还有一个重要的作用就是避免了缓冲池中页的频繁更改。再没有启用mrr优化之前,由于主键的id是随机取的,那么可能每次取到的数据都不在同一个页中。比如第一个数据在一个页中,第二个数据又在另一个页中,而第三个数据又在第一个页中,在缓冲池不够大的时候,这样的情况会造成缓冲池中的页不断的离开缓冲池,然后又进入缓冲池,从而造成缓冲池的热点数据频繁更新。而启用了mrr优化以后,由于主键是顺序的,则可以把此开销降为最低。
上面介绍了mrr优化,下面来看icp优化。在对具体能够解决什么问题之前,先来说明下icp优化的基本含义。icp优化的主要作用就是把索引的判断提前一步执行,这样就可以把数据的过滤放在存储引擎层,从而减少sql层对下层数据的索取,从而达到性能优化的目的。下面通过实例来看下icp优化的作用范围和限制条件。
假设有这样的一张表
student | CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL DEFAULT '',
`score` tinyint(3) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
对name和score做了联合索引idx_ne_se,在mysql5.5上的执行计划如下:
mysql> explain select * from student where name="xiaoguo" and score > 60;
+----+-------------+---------+-------+---------------+-----------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+-----------+---------+------+------+--------------------------+
| 1 | SIMPLE | student | range | idx_ne_se | idx_ne_se | 303 | NULL | 3 | Using where; Using index |
+----+-------------
mysql5.6上的执行计划如下:
mysql> explain select * from student where name="xiaoguo" and score > 60;
+----+-------------+---------+-------+---------------+-----------+---------+------+------+-----------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+-----------+---------+------+------+-----------------------------------+
| 1 | SIMPLE | student | range | idx_ne_se | idx_ne_se | 303 | NULL | 3 | Using indexcondition; Using index |
+----+-------------
可见mysql在5.6上启用了Using indexcondition优化,在取出数据的时候name=“xiaoguo”的时候直接过滤了数据,把索引的判断提前执行了一次。
下面再来看一下单独对name做索引的情况。mysqk5.5上的执行计划如下:
mysql> explain select * from student where name="xiaoguo" and score > 60;
+----+-------------+---------+-------+---------------+-----------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+-----------+---------+------+------+--------------------------+
| 1 | SIMPLE | student | range | name | name | 200 | NULL | 3 | Using where |
+----+-------------
mysql5.6上的执行计划如下:
mysql> explain select * from student where name="xiaoguo" and score > 60;
+----+-------------+---------+-------+---------------+-----------+---------+------+------+-----------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+-----------+---------+------+------+-----------------------------------+
| 1 | SIMPLE | student | range | name | name | 200 | NULL | 3 | Using where |
可见这里mysql5.5和5.6的执行计划是一样的,因此icp优化能够在取出索引的同时过滤数据的条件是该数据必须是该索引覆盖的范围。
在对这两种优化做出概述之前,先来看下传统的mysql针对辅助索引查找数据实现的方法。这里主要介绍innodb存储引擎对于辅助索引的查找实现方式。我们知道,mysql在对辅助索引取数据的时候,先是通过索引页的叶子节点找到对应的主键id,再通过主键id找到对应的数据页,在数据页中最后通过二分查找找到对应的数据。这里我们设想一下,假如在一张表中对某个字段建立一个辅助索引,而这个字段有一些重复的数据,那么我们根据这个字段去做where条件的时候,势必每次取到的id不一定是顺序的,既然不会是顺序的,那么必然会产生一定的随机io。在计算机中,随机io的速度比顺序的io的速度慢很多,因为在一个柱面中,随机io必然会造成磁头的随机旋转,从而产生一定量的磁盘io,而顺序io则可降低到最低。面对这种情况,就是我们下面介绍的mrr优化大显身手的地方了。
上面已经说过,对非唯一的辅助索引查询,由于每次在辅佐索引页叶子节点上查找主键的id的时候不一定是顺序的,如果每次都通过查询出来的主键盘id去拿数据的话,就会产生我们上面所说的随机io的情况。mrr优化就在这里做了功夫,在通过辅佐索引页上拿到主键id后,并不是通过id直接去数据页中取数据。而是先通过排序算法,把取到的主键id按照从小到大的方式排序,然后再通过书签查找,取得对应的数据。这样就可以把随机io的情况降到最低。
上面说到的是mrr的一个好的方式,其实mrr优化还有一个重要的作用就是避免了缓冲池中页的频繁更改。再没有启用mrr优化之前,由于主键的id是随机取的,那么可能每次取到的数据都不在同一个页中。比如第一个数据在一个页中,第二个数据又在另一个页中,而第三个数据又在第一个页中,在缓冲池不够大的时候,这样的情况会造成缓冲池中的页不断的离开缓冲池,然后又进入缓冲池,从而造成缓冲池的热点数据频繁更新。而启用了mrr优化以后,由于主键是顺序的,则可以把此开销降为最低。
上面介绍了mrr优化,下面来看icp优化。在对具体能够解决什么问题之前,先来说明下icp优化的基本含义。icp优化的主要作用就是把索引的判断提前一步执行,这样就可以把数据的过滤放在存储引擎层,从而减少sql层对下层数据的索取,从而达到性能优化的目的。下面通过实例来看下icp优化的作用范围和限制条件。
假设有这样的一张表
student | CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL DEFAULT '',
`score` tinyint(3) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
对name和score做了联合索引idx_ne_se,在mysql5.5上的执行计划如下:
mysql> explain select * from student where name="xiaoguo" and score > 60;
+----+-------------+---------+-------+---------------+-----------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+-----------+---------+------+------+--------------------------+
| 1 | SIMPLE | student | range | idx_ne_se | idx_ne_se | 303 | NULL | 3 | Using where; Using index |
+----+-------------
mysql5.6上的执行计划如下:
mysql> explain select * from student where name="xiaoguo" and score > 60;
+----+-------------+---------+-------+---------------+-----------+---------+------+------+-----------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+-----------+---------+------+------+-----------------------------------+
| 1 | SIMPLE | student | range | idx_ne_se | idx_ne_se | 303 | NULL | 3 | Using indexcondition; Using index |
+----+-------------
可见mysql在5.6上启用了Using indexcondition优化,在取出数据的时候name=“xiaoguo”的时候直接过滤了数据,把索引的判断提前执行了一次。
下面再来看一下单独对name做索引的情况。mysqk5.5上的执行计划如下:
mysql> explain select * from student where name="xiaoguo" and score > 60;
+----+-------------+---------+-------+---------------+-----------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+-----------+---------+------+------+--------------------------+
| 1 | SIMPLE | student | range | name | name | 200 | NULL | 3 | Using where |
+----+-------------
mysql5.6上的执行计划如下:
mysql> explain select * from student where name="xiaoguo" and score > 60;
+----+-------------+---------+-------+---------------+-----------+---------+------+------+-----------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+-----------+---------+------+------+-----------------------------------+
| 1 | SIMPLE | student | range | name | name | 200 | NULL | 3 | Using where |
可见这里mysql5.5和5.6的执行计划是一样的,因此icp优化能够在取出索引的同时过滤数据的条件是该数据必须是该索引覆盖的范围。