MySQL中的Join 的算法(NLJ/BNL/BKA)

科技资讯 投稿 24800 0 评论

MySQL中的Join 的算法(NLJ/BNL/BKA)

微信公众号:Java随想录

目录
    摘要
  • 什么是Join
  • Index Nested-Loop Join
  • Block Nested-Loop Join
  • MRR & BKA
  • 总结

摘要

什么是Join

在MySQL中,Join是一种用于组合两个或多个表中数据的查询操作。Join操作通常基于两个表中的某些共同的列进行,这些列在两个表中都存在。MySQL支持多种类型的Join操作,如Inner Join、Left Join、Right Join、Full Join等。

SELECT *
FROM customers
INNER JOIN orders
ON customers.customer_id = orders.customer_id;

上面的查询将返回所有存在于“customers”和“orders”表中的“customer_id”列相同的行。

Index Nested-Loop Join

工作流程如图:

select * from t1 straight_join t2 on (t1.a=t2.a;

在这个语句里,假设t1 是驱动表,t2是被驱动表。我们来看一下这条语句的explain结果。

    从表t1中读入一行数据 R;
  1. 从数据行R中,取出a字段到表t2里去查找;
  2. 取出表t2中满足条件的行,跟R组成一行,作为结果集的一部分;
  3. 重复执行步骤1到3,直到表t1的末尾循环结束。

这个过程就跟我们写程序时的嵌套查询类似,并且可以用上被驱动表的索引,所以我们称之为“Index Nested-Loop Join”,简称NLJ

MySQL会选择使用另一个叫作“Block Nested-Loop Join”的算法,简称BNL

Block Nested-Loop Join

例如,下面这个语句:

select * from t1 straight_join t2 on (t1.a=t2.b;

字段b上是没有建立索引的。

    把表t1的数据读入线程内存join_buffer中,由于我们这个语句中写的是select *,因此是把整个表t1放入了内存;
  1. 扫描表t2,把表t2中的每一行取出来,跟join_buffer中的数据做对比,满足join条件的,作为结果集的一部分返回。

这条SQL语句的explain结果如下所示:

虽然Block Nested-Loop Join算法是全表扫描。但是是在内存中进行的判断操作,速度上会快很多。但是性能仍然不如NLJ。

如果放不下表t1的所有数据话,策略很简单,就是分段放。

    顺序读取数据行放入join_buffer中,直到join_buffer满了。
  1. 扫描被驱动表跟join_buffer中的数据做对比,满足join条件的,作为结果集的一部分返回。
  2. 清空join_buffer,重复上述步骤。

MRR & BKA

上篇文章里我们讲到了MRR(Multi-Range Read)。MySQL在5.6版本后引入了Batched Key Acess(BKA算法了。这个BKA算法,其实就是对NLJ算法的优化,BKA算法正是基于MRR。

我们可以从表t1里一次性地多拿些行出来,先放到一个临时内存,一起传给表t2。这个临时内存不是别人,就是join_buffer。

NLJ算法优化后的BKA算法的流程,如图所示:

如果要使用BKA优化算法的话,你需要在执行SQL语句之前,先设置

set optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';

其中,前两个参数的作用是要启用MRR。这么做的原因是,BKA算法的优化要依赖于MRR。

例如,对于这个语句:

select * from t1 join t2 on (t1.b=t2.b where t2.b>=1 and t2.b<=2000;

使用临时表的大致思路是:

    把表t2中满足条件的数据放在临时表tmp_t中;
  1. 为了让join使用BKA算法,给临时表tmp_t的字段b加上索引;
  2. 让表t1和tmp_t做join操作。

总结

在MySQL中,不管Join使用的是NLJ还是BNL总是应该使用小表做驱动表。更准确地说,在决定哪个表做驱动表的时候,应该是两个表按照各自的条件过滤,过滤完成之后,计算参与join的各个字段的总数据量,数据量小的那个表,就是“小表”,应该作为驱动表。应当尽量避免使用BNL算法,如果确认优化器会使用BNL算法,就需要做优化。优化的常见做法是,给被驱动表的join字段加上索引,把BNL算法转成BKA算法。对于不好在索引的情况,可以基于临时表的改进方案,提前过滤出小数据添加索引。


编程笔记 » MySQL中的Join 的算法(NLJ/BNL/BKA)

赞同 (142) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽