暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

【MySQL专栏】韩锋:MySQL的表间关联算法—BNL

宜信技术学院 2021-05-28
881

在MySQL中,多表关联一直是其处理不太好的地方。MySQL本身只支持一种表间关联方式,就是嵌套循环(Nested Loop)。如果关联表的规模较大,则执行时间会非常长。在5.5以后的版本中,MySQL通过引入多种算法来优化嵌套执行。下面就介绍其中的一种,BlockNested-Loop。


1.准备工作

(1).创建结构

CREATE TABLE `big_emp` (

`empno` int(4) NOT NULL,

`ename` varchar(30) DEFAULT NULL,

`job` varchar(9) DEFAULT NULL,

`mgr` int(4) DEFAULT NULL,

`hiredate` date DEFAULT NULL,

`sal` int(7) DEFAULT NULL,

`comm` int(7) DEFAULT NULL,

`dname` varchar(13) DEFAULT NULL,

PRIMARY KEY (`empno`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `big_dept` (

`deptno` int(2) NOT NULL,

`dname` varchar(14) DEFAULT NULL,

`loc` varchar(13) DEFAULT NULL,

PRIMARY KEY (`deptno`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1;


(2).构造数据

seq 1000|awk '{print$1",dept"$1",loc"$1}'>big_dept.txt

seq -f%.0f 100000|awk '{print$1",user"$1",job,1,2000-01-01,1,1,dept"int(1000*rand())+1}'>big_emp.txt

LOAD DATA INFILE 'big_dept.txt' INTO TABLE big_dept FIELDS TERMINATED BY',' (deptno,dname,loc);

LOAD DATA INFILE 'big_emp.txt' INTO TABLE big_emp FIELDS TERMINATED BY ','(empno,ename,job,mgr,hiredate,sal,comm,dname);


2.默认情况 - Simple Nested-Loops Join

下面先来看看默认情况,观察一下MySQL是如何处理的。

(1).执行计划

explain select e.empno,e.ename,e.job,e.sal,d.dname from big_emp e,big_deptd where e.dname=d.dname\G;

********** 1. row **********

id: 1

select_type: SIMPLE

table: d

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 1000

Extra: NULL

********** 2. row **********

id: 1

select_type: SIMPLE

table: e

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 99865

Extra: Using where

这就是个简单的嵌套循环。外部表为big_dept,内部表为big_emp。外部表循环的每一条记录,在内部表进行匹配遍历。


(2).执行时长

mysql> select e.empno,e.ename,e.job,e.sal,d.dname from big_empe,big_dept d where e.dname=d.dname\G;

100000 rows in set (41.58 sec)

3. 优化算法 — Block Nested-Loop(块嵌套循环连接算法)

在5.5的环境里,可以使用优化的BNL算法来提高嵌套循环效率。下面看看MySQL是如何执行的。

(1).执行计划

mysql> set optimizer_switch='block_nested_loop=on';

Query OK, 0 rows affected (0.00 sec)

需要先打开一个开关,启用BNL算法。

mysql> explain select e.empno,e.ename,e.job,e.sal,d.dname from big_empe,big_dept d where e.dname=d.dname\G;

********** 1. row **********

id: 1

select_type: SIMPLE

table: d

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 1000

Extra: NULL

********** 2. row **********

id: 1

select_type: SIMPLE

table: e

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 99865

Extra: Using where; Usingjoin buffer (Block Nested Loop)

*在执行的Extra里中提示"Using join buffer",这就代表了使用了Block Nested LoopedJoin算法。


(2).执行时长

mysql> select e.empno,e.ename,e.job,e.sal,d.dname from big_empe,big_dept d where e.dname=d.dname\G;

100000 rows in set (7.93 sec)

对比上面的41秒,有4倍多的提升。


(3).算法说明

  • BNL是嵌套循环连接算法的改进算法。在代码中通过JOIN_CACHE_BNL类支持算法的实现。MySQL用此算法支持内连接、外连接、半连接的连接语义,其中外连接和半连接是在V5.6版本中被BNL算法支持的。

  • Simple Nested-LoopsJoin算法在内层循环时,外部表的每条记录都需要读取内部表一次。在内部表的连接上有索引的情况下,其扫描成本为O(Rn);若没有索引,则扫描成本为O(Rn*Sn)。如果内部表S有很多记录,则SimpleNested-Loops Join会扫描内部表很多次,执行效率非常差。而Block Nested-Loops Join算法就是针对没有索引的连接情况设置的,其使用JoinBuffer(连接缓冲)来减少内部循环读取表的次数。

  • 举例来说,BlockNested-Loops Join算法先把对Outer Loop表(外部表)每次读取的10行记录(准确的说是10行需要进行连接的列)放入Join Buffer中,然后在InnerLoop表(内部表)中直接匹配这10行数据。因此,对内部表的扫描减少了9/10。对于没有索引的表来说,Block Nested-Loops Join算法可以极大地提高连接的速度。


文章转载自宜信技术学院,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论