共计 2555 个字符,预计需要花费 7 分钟才能阅读完成。
MySQL 5.6 版本开始增加了提高表 join 性能的算法:Batched Key Access (BKA) 的新特性。
BKA 算法原理:将外层循环的行 / 结果集存入 join buffer,内存循环的每一行数据与整个 buffer 中的记录做比较,可以减少内层循环的扫描次数。
对于多表 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) 返回结果集给客户端。
对于多表 join 语句,当 MySQL 使用索引访问第二个 join 表的时候,使用一个 join buffer 来收集第一个操作对象生成的相关列值。BKA 构建好 key 后,批量传给引擎层做索引查找。key 是通过 MRR 接口提交给引擎 的(mrr 目的是较为顺序)。这样,MRR 使得查询更有效率。
大致的过程如下:
1 BKA 使用 join buffer 保存由 join 的第一个操作产生的符合条件的数据
2 然后 BKA 算法构建 key 来访问被连接的表,并批量使用 MRR 接口提交 keys 到数据库存储引擎去查找查找。
3 提交 keys 之后,MRR 使用最佳的方式来获取行并反馈给 BKA
BNL(Block Nested Loop) 和 BKA(MySQL Batched Key Access) 都是批量的提交一部分行给被 join 的表,从而减少访问的次数,那么它们有什么区别呢?
第一 BNL 比 BKA 出现的早,BKA 直到 5.6 才出现,而 BNL 至少在 5.1 里面就存在。
第二 BNL 主要用于当被 join 的表上无索引
第三 BKA 主要是指在被 join 表上有索引可以利用,那么就在行提交给被 join 的表之前,对这些行按照索引字段进行排序,因此减少了随机 IO,排序这才是两者最大的区别,但是如果被 join 的表没用索引呢?那就使用 BNL 了。
以下设置启用 BKA:
要使用 BKA,必须调整系统参数 optimizer_switch 的值,官方推荐关闭 mrr_cost_based,应将其设置为 off。
mysql> SET global optimizer_switch=’mrr=on,mrr_cost_based=off,batched_key_access=on’;
备注:
BKA 主要适用于 join 的表上有索引可利用,无索引只能使用 BNL。
多表 join 语句,被 join 的表 / 非驱动表必须有索引可用。
在 EXPLAIN 输出中,当 Extra 值包含 Using join buffer(Batched Key Access),表示使用 BKA。
+--------------------------------------------------------+
| Extra |
+--------------------------------------------------------+
| NULL |
| Using where; Using join buffer (Batched Key Access) |
+--------------------------------------------------------+
使用 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)
: