Mysql JOIN算法解析

1.Index Nested-Loop Join(NLJ)

测试所用t1、t2表结构都如下

CREATE TABLE `t2` (
  `id` int(11) NOT NULL,
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `a` (`a`)
) ENGINE=InnoDB;

select * from t1 straight_join t2 on (t1.a=t2.a); 
/*使用join语句的话,需要让小表做驱动表,这样才能取得比较好的性能,具体原因如后文描述,为了防止优化器自己决定谁做驱动表,这里我们使用straight_join 指定t1作为驱动表 */

c977d6a11f5f679726893e85b18963e3.pngc977d6a11f5f679726893e85b18963e3.png

这个连接的过程是先遍历表t1,然后根据从表t1中取出的每行数据中的a值,去表t2中查找满足条件的记录。在形式上,这个过程就跟我们写程序时的嵌套查询类似,并且可以用上被驱动表的索引,所以我们称之为“Index Nested-Loop Join”,简称NLJ。

2.Block Nested-Loop Join

这个block的意思是把驱动表切割成一块一块的,每个块的大小由join_buffer的值决定,然后把每个块存放到内存(join_bufffer)中(只存放select子句指定的列),最后再扫描被驱动表,把它和驱动表做比对,看是否满足join条件,如果满足条件,则作为结果集的一部分返回。
过程如下图所示
184ebf48305f9f78550739f1fe0e75ae.png184ebf48305f9f78550739f1fe0e75ae.png

3.使用MRR算法加速回表

MRR( multi_range_read) 可加速普通索引的回表过程,它的原理是
因为大多数的数据都是按照主键递增顺序插入得到的,所以我们可以认为,如果按照主键的递增顺序查询的话,对磁盘的读比较接近顺序读,能够提升读性能
为了使用MRR算法,需要做如下设置
set optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on'
正常情况下,是索引回表一行一行的回表,使用MRR算法后

  1. 在二级索引中找到主键值后,放入到 read_rnd_buffer 中,
  2. 将read_rnd_buffer中的主键id进行递增排序;
  3. 排序后的id数组,依次到主键id索引中查记录,并作为结果返回。

如果read_rnd_buffer放满了,就会先执行完步骤2和3,然后清空read_rnd_buffer。之后继续找索引a的下个记录,并继续循环。
07c9824e358ed6f3d479aa059f76cd66.png07c9824e358ed6f3d479aa059f76cd66.png
执行计划的extra字段会显示using MRR
510d96a8bf61be484d2acfa04e189fc5.png510d96a8bf61be484d2acfa04e189fc5.png

4.Batched Key Access(BKA)

BKA算法,是对NLJ算法的优化,

NLJ算法执行的逻辑是:从驱动表t1,一行行地取出a的值,再到被驱动表t2去做join。也就是说,对于表t2来说,每次都是匹配一个值,如果能一次性能传多个值给t2,那么就能够利用MRR的优势,转随机读为相对有序的连续读

 
t1join_buffert2读取t1所需字段,按照t2.b排序join 条件比对,取到结果数据t1join_buffert2

如果要使用BKA优化算法的话,你需要在执行SQL语句之前,先设置
set optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';

另外需要说明的是,如果你想要稳定地使用MRR优化的话,需要设置set optimizer_switch="mrr_cost_based=off"。(官方文档的说法,是现在的优化器策略,判断消耗的时候,会更倾向于不使用MRR,把mrr_cost_based设置为off,就是固定使用MRR了。)

极客时间 林晓斌 《mysql实战45讲》

猜你喜欢

转载自www.cnblogs.com/ligfoo/p/12627840.html