0
点赞
收藏
分享

微信扫一扫

2022-11-11 mysql-表间关联算法—BNL


在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_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

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

(2).执行时长

mysql> select e.empno,e.ename,e.job,e.sal,d.dname from big_emp e,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_emp e,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_emp e,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算法可以极大地提高连接的速度。

逻辑跟踪:

JOIN_CACHE::write_record_data
 

(gdb) bt
#0 JOIN_CACHE::write_record_data (this=0x7f8790905f68, link=0x0, is_full=0x7faac99a5847) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_join_buffer.cc:1251
#1 0x00000000025c6dc4 in JOIN_CACHE::put_record_in_cache (this=0x7f8790905f68) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_join_buffer.cc:1401
#2 0x00000000025cae81 in JOIN_CACHE::put_record (this=0x7f8790905f68) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_join_buffer.h:426
#3 0x00000000023520c0 in sub_select_op (join=0x7f8790905270, qep_tab=0x7f8790905df0, end_of_records=false) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_executor.cc:1014
#4 0x0000000002353008 in evaluate_join_record (join=0x7f8790905270, qep_tab=0x7f8790905c78) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_executor.cc:1559
#5 0x0000000002352437 in sub_select (join=0x7f8790905270, qep_tab=0x7f8790905c78, end_of_records=false) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_executor.cc:1225
#6 0x0000000002351c92 in do_select (join=0x7f8790905270) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_executor.cc:885
#7 0x000000000234fbfb in JOIN::exec (this=0x7f8790905270) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_executor.cc:192
#8 0x00000000023e8c43 in handle_query (thd=0x7f8790002c00, lex=0x7f8790004f28, result=0x7f8790900ce0, added_options=0, removed_options=0, optimize_after_bh=0, free_join_from_bh=0)
at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_select.cc:191
#9 0x000000000239ee6e in execute_sqlcom_select (thd=0x7f8790002c00, all_tables=0x7f87909040e0) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_parse.cc:4853
#10 0x00000000023981b8 in mysql_execute_command (thd=0x7f8790002c00, first_level=true) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_parse.cc:2655
#11 0x000000000239fdfd in mysql_parse (thd=0x7f8790002c00, parser_state=0x7faac99a6e70) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_parse.cc:5254
#12 0x0000000002395095 in dispatch_command (thd=0x7f8790002c00, com_data=0x7faac99a7610, command=COM_QUERY) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_parse.cc:1399
#13 0x0000000002393fc1 in do_command (thd=0x7f8790002c00) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_parse.cc:976
#14 0x00000000024c6bc1 in handle_connection (arg=0x187b3c20) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/conn_handler/connection_handler_per_thread.cc:313
#15 0x0000000002bac33c in pfs_spawn_thread (arg=0x188097e0) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/storage/perfschema/pfs.cc:2197
#16 0x00007fab19fa9ea5 in start_thread () from /lib64/libpthread.so.0
#17 0x00007fab17226b0d in clone () from /lib64/libc.so.6

向buffer中写入数据:

2022-11-11 mysql-表间关联算法—BNL_数据库

(gdb) p copy[0]
$4 = {
str = 0x7f5d780120ad "\005dept1",
length = 15,
field = 0x7f5d780183d8,
type = 3,
referenced_field_no = 0,
next_copy_rowid = 0x0,
blob_length = 2408550287,
offset = 2408550287
}

(gdb) p cp
$5 = (uchar *) 0x7f5d7801b801 "\005dept1\376\005dept2\376\005dept3\376\005dept4\376\005dept5"

JOIN_CACHE::read_record_field
 

(gdb) bt
#0 JOIN_CACHE::read_record_field (this=0x7f8790905f68, copy=0x7f87909071d0, blob_in_rec_buff=false) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_join_buffer.cc:1610
#1 0x00000000025c712f in JOIN_CACHE::read_some_record_fields (this=0x7f8790905f68) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_join_buffer.cc:1555
#2 0x00000000025c6e96 in JOIN_CACHE::get_record (this=0x7f8790905f68) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_join_buffer.cc:1442
#3 0x00000000025c8076 in JOIN_CACHE_BNL::join_matching_records (this=0x7f8790905f68, skip_last=false) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_join_buffer.cc:2050
#4 0x00000000025c79cb in JOIN_CACHE::join_records (this=0x7f8790905f68, skip_last=false) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_join_buffer.cc:1849
#5 0x00000000025caf35 in JOIN_CACHE::end_send (this=0x7f8790905f68) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_join_buffer.h:455
#6 0x0000000002352012 in sub_select_op (join=0x7f8790905270, qep_tab=0x7f8790905df0, end_of_records=true) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_executor.cc:1000
#7 0x000000000235214a in sub_select (join=0x7f8790905270, qep_tab=0x7f8790905c78, end_of_records=true) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_executor.cc:1154
#8 0x0000000002351cbe in do_select (join=0x7f8790905270) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_executor.cc:887
#9 0x000000000234fbfb in JOIN::exec (this=0x7f8790905270) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_executor.cc:192
#10 0x00000000023e8c43 in handle_query (thd=0x7f8790002c00, lex=0x7f8790004f28, result=0x7f8790900ce0, added_options=0, removed_options=0, optimize_after_bh=0, free_join_from_bh=0)
at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_select.cc:191
#11 0x000000000239ee6e in execute_sqlcom_select (thd=0x7f8790002c00, all_tables=0x7f87909040e0) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_parse.cc:4853
#12 0x00000000023981b8 in mysql_execute_command (thd=0x7f8790002c00, first_level=true) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_parse.cc:2655
#13 0x000000000239fdfd in mysql_parse (thd=0x7f8790002c00, parser_state=0x7faac99a6e70) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_parse.cc:5254
#14 0x0000000002395095 in dispatch_command (thd=0x7f8790002c00, com_data=0x7faac99a7610, command=COM_QUERY) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_parse.cc:1399
#15 0x0000000002393fc1 in do_command (thd=0x7f8790002c00) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/sql_parse.cc:976
#16 0x00000000024c6bc1 in handle_connection (arg=0x187b3c20) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/sql/conn_handler/connection_handler_per_thread.cc:313
#17 0x0000000002bac33c in pfs_spawn_thread (arg=0x188097e0) at /home/jenkins/workspace/stonedb5.7-zsl-centos7.9-30-119/storage/perfschema/pfs.cc:2197
#18 0x00007fab19fa9ea5 in start_thread () from /lib64/libpthread.so.0
#19 0x00007fab17226b0d in clone () from /lib64/libc.so.6

从buffer中读出的数据

2022-11-11 mysql-表间关联算法—BNL_centos_02

(gdb) p len
$31 = 6
(gdb) p pos
$32 = (uchar *) 0x7f879090ee01 "\005dept1\376\005dept2\376\005dept3\376\005dept4\376\005dept5"
(gdb)

举报

相关推荐

0 条评论