- MRR-Multi-Range Read
- 主键索引是一颗B+树,在这棵树上,每次只能根据一个主键id查到一行数据。因此,回表肯定是一行行搜索主键索引的。

- 如果随着a的值递增查询的话,id的值就变成随机的,那么就会出现随机访问,性能相对较差。虽然’按行查’这个机制不能改,但是调整查询的顺序,还是能够加速的。
- 因为大多数的数据都是按照主键递增顺序插入得到的,如果按照主键的递增顺序查询的话,对磁盘的读比较接近顺序读,能够提升读性能。
- mrr优化的设计思路,语句的执行流程变为:
- 根据索引a,定位到满足条件的记录,将id值放入read_rnd_buffer中。
- 将read_rnd_buffer中的id进行递增排序。
- 排序后的id数组,一次到主键id索引中查记录,并作为结果返回。
- read_rnd_buffer的大小是有read_rnd_buffer_size参数控制的。如果步骤1中,read_rnd_buffer放满了,就会先执行 2、3步,然后清空read_rnd_buffer。之后继续找索引a的下个记录,并继续循环。

- MRR能够提升性能的核心在于,这条查询语句在索引a上做的是一个范围查询(也就是多值查询),可以得到足够多的主键id。这样通过排序以后,再去主键索引查数据,才能体现出’顺序性’的优势。
- Batched Key Access - 对NLJ算法的优化。

- NLJ算法执行逻辑是:从驱动表t1,一行行的取出a的值,再到驱动表t2去做join。也就是说,对于表t2来说,每次都是匹配一个值,这时,MRR的优势就用不上了。
- 把表t1的数据取出来一部分,先放到一个临时内存,这个临时内存不是别人,就是join_buffer。

- 图中,join_buffer中放入的数据是P1-P100,表示的是只会取查询需要的字段。当然,如果join_buffer放不下P1-P100的所有数据,就会把这100行数据分成多段执行上图的流程。
- BKA算法的优化依赖于MRR。
- BNL算法优化
- LRU-由于innodb对Buffer Pool的lru算法做了优化,第一次从磁盘读入内存的数据页,会先放在old区域。如果1秒之后这个数据页不再被访问了,就不会被移动到LRU链表头部,这样对Buffer Pool的命中率影响就不大。
- 如果一个使用BNL算法的join语句,多次扫描一个冷表,而且这个语句执行时间超过1秒,就会再次扫描冷表的时候,把冷表的数据页移到LRU链表头部。
- 这种情况对应的,是冷表的数据量小于整个Buffer Pool的3/8,能够完全放入old区域的情况。
- 如果这个冷表很大,就会出现另一种情况,业务正常访问的数据页,没有机会进入young区域。
- BNL算法对系统的主要影响
- 可能会多次扫描被驱动表,占用磁盘IO资源。
- 判断join条件需要执行M*N次对比(M、N分别是两张表的行数),如果是大表就会占用非常多的CPU资源。
- 可能会导致Buffer Pool的热数据被淘汰,影响内存命中率。
- BNL转BKA
- 一些情况下,直接在被驱动表上建索引,这时就可以直接转成BKA算法了。
- 使用临时表。
- 把表t2中满足条件的数据放在临时表tmp_t中。
- 为了让join使用BKA算法,给临时表tmp_t的字段b加上索引。
- 让表t1和tmp_t做join操作。
- BKA优化是mysql已经内置支持的,建议默认使用。
- BNL算法效率低,尽量转成BKA算法,优化方向就是给被驱动表的关联字段加上索引。
- 基于临时表的改进方案,对于能够提前过滤出小数据的join语句来说,效果很好。


