加载中...

MySQL的MRR优化


概念定义

MRR,英文全名为Multi-Range Read Optimization,看这个名字,就可以联想到,这个优化是针对多个范围查询的读优化。该优化时MySQL5.6版本新增的特性,MySQL官网上的说明为:

‎在二级索引上使用范围扫描读取行,如果该表很大且未存储在存储引擎的缓存中,可能会导致许多随机磁盘访问。通过MRR优化,MySQL尝试通过首先仅扫描索引并收集相关行的键来减少范围扫描的随机磁盘访问次数。然后对键进行排序,最后按照排列好的顺序回表中检索行。磁盘扫描 MRR 的动机是减少随机磁盘访问的次数,实现对表数据的更连续的扫描。

简单来说,MRR通过把随机磁盘读,转化为磁盘顺序读,从而提高了索引查询的性能。

机械硬盘的特点

传统的磁盘由大小相同且同轴的圆形盘片组成,如下所示:

盘片被划分成一系列同心环,每个同心环叫做一个磁道,所有半径相同的磁道组成一个柱面。磁道被沿半径线划分成一个个小的段,每个段叫做一个扇区,每个扇区是磁盘的最小存储单元,即如下所示:

读取数据时,磁盘控制电路根据得到的逻辑地址转换成实际物理地址找到数据位于哪一个扇区的哪一条磁道,磁头接着移动至对应扇区中的指定磁道,这个过程称为寻道。因为是机械运动,所以寻道时间一般比较费时,如果每次读取数据都是随机的,不是连续的,那么这些机械运动将极大降低读取数据的效率。

范围查询流程

首先我们假设有这样一张表

CREATE TABLE `score` (
  `id` varchar(20) NOT NULL,
  `name` varchar(20) NOT NULL,
  `s_score` int DEFAULT NULL,
  PRIMARY KEY (`id`)
  KEY `idx_score` (`s_score`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我们执行一个范围查询:

select * from score where s_score between 60 and 70;

假设我们没有开启MRR时,这个sql被执行的时候,MySQL执行的流程将会是如下图所示:

由于Innodb存储数据的方式:辅助索引的存储顺序并非与主键的顺序一致,从图中可以看出,根据辅助索引获取的主键来访问表中的数据会导致随机的 IO 。不同主键不在同一个 page(MySQL读取数据的基本单元)里面时必然导致多次 IO 和随机读。

其实由于MyISAM引擎没有聚簇索引,该引擎下这种范围查询问题将会出现得更为频繁。

MRR优化流程

由上可知,随机读取数据将导致低效得性能,所以MRR优化就是将这个随机读改成了顺序读,具体流程如下:

首先,通过二级索引得到一个二级索引与主键索引的数据集合,将集合放入buffer(read_rnd_buffer_size控制该buffer大小)中,接着将buffer内的数据按照主键索引值排序,最后根据顺序进行IO访问磁盘的数据,这样就是顺序读了。

顺序读总的来说,主要有一下几个好处:

1、减少磁头机械运动

2、充分利用磁盘预读(局部性原理)

  • 比如请求一页的数据时,可以把后面几页的数据也一起返回,放到数据缓冲池中,这样如果下次刚好需要下一页的数据,就不再需要到磁盘读取。

3、每一页的数据只会读取一次

  • 因为buffer中的数据已经按照主键索引排序过了,所以如果存在两个数据行在同一页,那么一定是将这个数据页拿到MySQL缓存之后就全部获取到了,而不会出现1->2->3->4->1这样的数据页的数据读取,也就不会出现缓存页被剔除后又要重新拿到该缓存页的情况了(类似虚拟内存的FIFO的多次swap)。

总结

MRR本质是一种空间换时间的算法,read_rnd_buffer_size不可能设置为无限大来进行排序,实际中要根据具体情况来具体配置。

参考

MySQL :: MySQL 5.6 Reference Manual :: 8.2.1.10 Multi-Range Read Optimization

MySQL Multi-Range Read

CodingLabs - MySQL索引背后的数据结构及算法原理


文章作者: DestiNation
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 DestiNation !
  目录