MySQL 查询优化之 Block Nested-Loop 与 Batched Key Access Joins

在MySQL中,可以使用批量密钥访问(BKA)连接算法,该算法使用对连接表的索引访问和连接缓冲区。

BKA算法支持:内连接,外连接和半连接操作,包括嵌套外连接。

BKA的优点:更加高效的表扫描提高了连接性能。

此外,先前仅用于内连接的块嵌套循环(BNL)连接算法现已扩展,可用于外连接半连接操作,包括嵌套外连接

以下部分讨论了连接缓冲区管理,它是原始BNL算法扩展,扩展BNL算法和BKA算法的基础。 有关半连接策略的信息,请参见“使用半连接转换优化子查询,派生表和视图引用”

Nested Loop Join算法

将外层表的结果集作为循环的基础数据,然后循环从该结果集每次一条获取数据作为下一个表的过滤条件去查询数据,然后合并结果。如果有多个表join,那么应该将前面的表的结果集作为循环数据,取结果集中的每一行再到下一个表中继续进行循环匹配,获取结果集并返回给客户端。

伪代码如下

for each row in t1 matching range {
for each row in t2 matching reference key {
for each row in t3 {
if row satisfies join conditions,
send to client
}
}
}

普通的Nested-Loop Join算法一次只能将一行数据传入内存循环,所以外层循环结果集有多少行,那么内存循环就要执行多少次。

Block Nested-Loop算法

MySQL BNL算法原本只支持内连接,现在已支持外连接半连接操作,包括嵌套外连接

BNL算法原理:将外层循环的行/结果集存入join buffer,内存循环的每一行数据与整个buffer中的记录做比较,可以减少内层循环的扫描次数

举个简单的例子:外层循环结果集有1000行数据,使用NLJ算法需要扫描内层表1000次,但如果使用BNL算法,则先取出外层表结果集的100行存放到join buffer, 然后用内层表的每一行数据去和这100行结果集做比较,可以一次性与100行数据进行比较,这样内层表其实只需要循环1000/100=10次,减少了9/10。

伪代码如下

for each row in t1 matching range {
for each row in t2 matching reference key {
store used columns from t1, t2 in join buffer
if buffer is full {
for each row in t3 {
for each t1, t2 combination in join buffer {
if row satisfies join conditions,
send to client
}
}
empty buffer
}
}
} if buffer is not empty {
for each row in t3 {
for each t1, t2 combination in join buffer {
if row satisfies join conditions,
send to client
}
}
}

如果t1, t2参与join的列长度只和为s, c为二者组合数, 那么t3表被扫描的次数为

(S * C)/join_buffer_size + 1

扫描t3的次数随着join_buffer_size的增大而减少, 直到join buffer能够容纳所有的t1, t2组合, 再增大join buffer size, query 的速度就不会再变快了。

optimizer_switch系统变量的block_nested_loop标志控制优化器是否使用块嵌套循环算法。

默认情况下,block_nested_loop已启用。

在EXPLAIN输出中,当Extra值包含Using join buffer(Block Nested Loop)type值为ALL,index或range时,表示使用BNL。

示例

mysql> explain SELECT  a.gender, b.dept_no FROM employees a, dept_emp b WHERE a.birth_date = b.from_date;
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+
| 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 298936 | 100.00 | NULL |
| 1 | SIMPLE | b | NULL | ALL | NULL | NULL | NULL | NULL | 331143 | 10.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

Batched Key Access 算法

对于多表join语句,当MySQL使用索引访问第二个join表的时候,使用一个join buffer来收集第一个操作对象生成的相关列值。BKA构建好key后,批量传给引擎层做索引查找。key是通过MRR接口提交给引擎的,这样,MRR使得查询更有效率。

如果外部表扫描的是主键,那么表中的记录访问都是比较有序的,但是如果联接的列是非主键索引,那么对于表中记录的访问可能就是非常离散的。因此对于非主键索引的联接,Batched Key Access Join算法将能极大提高SQL的执行效率。BKA算法支持内连接,外连接和半连接操作,包括嵌套外连接。

Batched Key Access Join算法的工作步骤如下:

  • 1) 将外部表中相关的列放入Join Buffer中。

  • 2) 批量的将Key(索引键值)发送到Multi-Range Read(MRR)接口。

  • 3) Multi-Range Read(MRR)通过收到的Key,根据其对应的ROWID进行排序,然后再进行数据的读取操作。

  • 4) 返回结果集给客户端。

Batched Key Access Join算法的本质上来说还是Simple Nested-Loops Join算法,其发生的条件为内部表上有索引,并且该索引为非主键,并且联接需要访问内部表主键上的索引。这时Batched Key Access Join算法会调用Multi-Range Read(MRR)接口,批量的进行索引键的匹配和主键索引上获取数据的操作,以此来提高联接的执行效率,因为读取数据是以顺序磁盘IO而不是随机磁盘IO进行的。

使用BKA时,join_buffer_size的值定义了对存储引擎的每个请求中批量密钥的大小。缓冲区越大,对连接操作的右侧表的顺序访问就越多,这可以显着提高性能。

要使用BKA,必须将optimizer_switch系统变量的batched_key_access标志设置为on。 BKA使用MRR,因此mrr标志也必须打开。目前,MRR的成本估算过于悲观。因此,mrr_cost_based也必须关闭才能使用BKA。

以下设置启用BKA:

mysql> SET optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';

在EXPLAIN输出中,当Extra值包含Using join buffer(Batched Key Access)且类型值为refeq_ref时,表示使用BKA。

示例:

mysql> show index from employees;
+-----------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| employees | 0 | PRIMARY | 1 | emp_no | A | 298936 | NULL | NULL | | BTREE | | |
| employees | 1 | idx_name | 1 | last_name | A | 1679 | NULL | NULL | | BTREE | | |
| employees | 1 | idx_name | 2 | first_name | A | 277495 | NULL | NULL | | BTREE | | |
| employees | 1 | idx_birth_date | 1 | birth_date | A | 4758 | NULL | NULL | | BTREE | | |
+-----------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (0.00 sec) mysql> explain SELECT a.gender, b.dept_no FROM employees a, dept_emp b WHERE a.birth_date = b.from_date;
+----+-------------+-------+------------+------+----------------+----------------+---------+-----------------------+--------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+----------------+----------------+---------+-----------------------+--------+----------+-------+
| 1 | SIMPLE | b | NULL | ALL | NULL | NULL | NULL | NULL | 331143 | 100.00 | NULL |
| 1 | SIMPLE | a | NULL | ref | idx_birth_date | idx_birth_date | 3 | employees.b.from_date | 62 | 100.00 | NULL |
+----+-------------+-------+------------+------+----------------+----------------+---------+-----------------------+--------+----------+-------+ #使用hint,强制走bka mysql> explain SELECT /*+ bka(a)*/ a.gender, b.dept_no FROM employees a, dept_emp b WHERE a.birth_date = b.from_date;
+----+-------------+-------+------------+------+----------------+----------------+---------+-----------------------+--------+----------+----------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+----------------+----------------+---------+-----------------------+--------+----------+----------------------------------------+
| 1 | SIMPLE | b | NULL | ALL | NULL | NULL | NULL | NULL | 331143 | 100.00 | NULL |
| 1 | SIMPLE | a | NULL | ref | idx_birth_date | idx_birth_date | 3 | employees.b.from_date | 62 | 100.00 | Using join buffer (Batched Key Access) |
+----+-------------+-------+------------+------+----------------+----------------+---------+-----------------------+--------+----------+----------------------------------------+
2 rows in set, 1 warning (0.00 sec)

BNL和BKA算法的优化器Hint

除了使用optimizer_switch系统变量来控制优化程序在会话范围内使用BNL和BKA算法之外,MySQL还支持优化程序提示,以便在每个语句的基础上影响优化程序。 请参见“优化程序Hint”

要使用BNL或BKA提示为外部联接的任何内部表启用联接缓冲,必须为外部联接的所有内部表启用联接缓冲。

使用qb_name

SELECT /*+ QB_NAME(qb1) MRR(@qb1 t1) BKA(@qb2) NO_MRR(@qb3t1 idx1, id2) */ ...
FROM (SELECT /*+ QB_NAME(qb2) */ ...
FROM (SELECT /*+ QB_NAME(qb3) */ ... FROM ...)) ...

MySQL 查询优化之 Block Nested-Loop 与 Batched Key Access Joins的更多相关文章

  1. MySQL Block Nested Loop and Batched Key Access Joins(块嵌套循环和批量Key访问连接)

    Block Nested-Loop and Batched Key Access Joins Batched Key Access (BKA) Join算法通过index和join buffer访问j ...

  2. 解决:Using where; Using join buffer (Block Nested Loop)

    问题:left join 时候触发了全表查询导致很慢 解决:Using where; Using join buffer (Block Nested Loop) 总结:其实就是把left join 改 ...

  3. Block Nested-Loop 和 Batched Key Access

    官方文档:https://dev.mysql.com/doc/refman/5.7/en/bnl-bka-optimization.html BNL和BKA是MySQL 表关联的两种关联算法 比如t1 ...

  4. MySQL Batched Key Access

    Batched Key Access是MySQL 5.6 版本中的新特性,是一种用户提高表join性能的算法.[Batched Key Access]       对于多表join语句,当MySQL使 ...

  5. mysql 查询优化案例

    mysql> explain SELECT c.`sn` clientSn,asm.`clientManagerSn`,pry.`productSn`,1 TYPE,pr.`capitalBal ...

  6. mysql 查询优化案例汇总

    一 简介:此文章为经历过的sql案例集合和相关思路 二 案例1: 现象: 测试环境出现select语句,join2张表多次join,explain结果如下 出现 using where,using j ...

  7. mysql 查询优化~join算法

    一简介:参考了几位师兄,尤其是M哥大神的博客,让我恍然大悟,赶紧记录下二 原理: mysql的三种算法 1 Simple Nested-Loop Join 将驱动表/外部表的结果集作为循环基础数据,然 ...

  8. 44 答疑(三)--join的写法/Simple nested loop join的性能问题/Distinct和group by的性能/备库自增主键问题

    44 答疑(三) Join的写法 35节介绍了join执行顺序,加了straight_join,两个问题: --1 如果用left join,左边的表一定是驱动表吗 --2 如果两个表的join包含多 ...

  9. 1122MySQL性能优化之 Nested Loop Join和Block Nested-Loop Join(BNL)

    转自http://blog.itpub.net/22664653/viewspace-1692317/ 一 介绍  相信许多开发/DBA在使用MySQL的过程中,对于MySQL处理多表关联的方式或者说 ...

随机推荐

  1. PHP composer 日常使用命令和理解

    composer的操作很多很杂,平时大都用不上,正要找又一时半会难找到 日常操作,走起 第一部分 : 安装 composer.json composer init 这个命令创建了一个 composer ...

  2. oracle view and MATERIALIZED VIEW

    View http://blog.csdn.net/tianlesoftware/article/details/5530618 MATERIALIZED VIEW http://blog.csdn. ...

  3. MySQL 查询练习记录

    MySQL 查询练习记录 最近在复习mysql,在b站上找了一个感觉还不错的视频,把视频中查询练习相关的内容记录了下来,以便自己日后查阅和复习. 视频连接:https://www.bilibili.c ...

  4. redis最佳实践

    总结: String类型的value(string/list/set/hash)使用StringRedisTemplate 其他类型的value(string/list/set/hash/object ...

  5. CellSet 遍历

    CellSet 结构: 查询MDX: SELECT NON EMPTY {{ {{ {{ {{ {{ AddCalculatedMembers([店铺.店铺ID].[店铺ID].Members)}} ...

  6. Exception sending context destroyed event to listener instance of class

    五月 29, 2019 6:29:39 下午 org.apache.catalina.core.StandardContext listenerStop严重: Exception sending co ...

  7. c#基础-构造函数 this new

    构造函数作用:帮助我们初始化对象(给对象的每个属性依次的赋值)构造函数是一个特殊的方法:1).构造函数没有返回值,连void也不能写.2).构造函数的名称必须跟类名一样. 创建对象的时候会执行构造函数 ...

  8. 利用wsdl.exe生成webservice代理类

    通常要手动生成WebService代理类需要把一句生成语句,如 wsdl.exe /l:cs /out:D:\Proxy_UpdateService.cs  http://localhost:1101 ...

  9. JavaScprit30-6 学习笔记

    今天学习的是  仿即时搜索诗句效果 第一个问题: fetch() Fetch API  提供了一个 JavaScript接口,用于访问和操纵HTTP管道的部分,例如请求和响应.它还提供了一个全局 fe ...

  10. 帝国empirecms去除后台登陆认证码

    打开文件:\e\config\config.php 找到代码 $ecms_config['esafe']['loginauth']='abc'; 把值设为空即可,即改为 $ecms_config['e ...