1. MRR-Multi-Range Read
    2. 主键索引是一颗B+树,在这棵树上,每次只能根据一个主键id查到一行数据。因此,回表肯定是一行行搜索主键索引的。
      1. image.png
      2. 如果随着a的值递增查询的话,id的值就变成随机的,那么就会出现随机访问,性能相对较差。虽然’按行查’这个机制不能改,但是调整查询的顺序,还是能够加速的。
    3. 因为大多数的数据都是按照主键递增顺序插入得到的,如果按照主键的递增顺序查询的话,对磁盘的读比较接近顺序读,能够提升读性能。
    4. mrr优化的设计思路,语句的执行流程变为:
      1. 根据索引a,定位到满足条件的记录,将id值放入read_rnd_buffer中。
      2. 将read_rnd_buffer中的id进行递增排序。
      3. 排序后的id数组,一次到主键id索引中查记录,并作为结果返回。
      4. read_rnd_buffer的大小是有read_rnd_buffer_size参数控制的。如果步骤1中,read_rnd_buffer放满了,就会先执行 2、3步,然后清空read_rnd_buffer。之后继续找索引a的下个记录,并继续循环。
      5. image.png
    5. MRR能够提升性能的核心在于,这条查询语句在索引a上做的是一个范围查询(也就是多值查询),可以得到足够多的主键id。这样通过排序以后,再去主键索引查数据,才能体现出’顺序性’的优势。
    6. Batched Key Access - 对NLJ算法的优化。
      1. image.png
      2. NLJ算法执行逻辑是:从驱动表t1,一行行的取出a的值,再到驱动表t2去做join。也就是说,对于表t2来说,每次都是匹配一个值,这时,MRR的优势就用不上了。
      3. 把表t1的数据取出来一部分,先放到一个临时内存,这个临时内存不是别人,就是join_buffer。
      4. image.png
        1. 图中,join_buffer中放入的数据是P1-P100,表示的是只会取查询需要的字段。当然,如果join_buffer放不下P1-P100的所有数据,就会把这100行数据分成多段执行上图的流程。
        2. BKA算法的优化依赖于MRR。
    7. BNL算法优化
      1. LRU-由于innodb对Buffer Pool的lru算法做了优化,第一次从磁盘读入内存的数据页,会先放在old区域。如果1秒之后这个数据页不再被访问了,就不会被移动到LRU链表头部,这样对Buffer Pool的命中率影响就不大。
      2. 如果一个使用BNL算法的join语句,多次扫描一个冷表,而且这个语句执行时间超过1秒,就会再次扫描冷表的时候,把冷表的数据页移到LRU链表头部。
        1. 这种情况对应的,是冷表的数据量小于整个Buffer Pool的3/8,能够完全放入old区域的情况。
        2. 如果这个冷表很大,就会出现另一种情况,业务正常访问的数据页,没有机会进入young区域。
      3. BNL算法对系统的主要影响
        1. 可能会多次扫描被驱动表,占用磁盘IO资源。
        2. 判断join条件需要执行M*N次对比(M、N分别是两张表的行数),如果是大表就会占用非常多的CPU资源。
        3. 可能会导致Buffer Pool的热数据被淘汰,影响内存命中率。
    8. BNL转BKA
      1. 一些情况下,直接在被驱动表上建索引,这时就可以直接转成BKA算法了。
      2. 使用临时表。
        1. 把表t2中满足条件的数据放在临时表tmp_t中。
        2. 为了让join使用BKA算法,给临时表tmp_t的字段b加上索引。
        3. 让表t1和tmp_t做join操作。
    9. BKA优化是mysql已经内置支持的,建议默认使用。
    10. BNL算法效率低,尽量转成BKA算法,优化方向就是给被驱动表的关联字段加上索引。
    11. 基于临时表的改进方案,对于能够提前过滤出小数据的join语句来说,效果很好。
      1. image.png
      2. image.png