美文网首页
mysql 联接查询算法之Batched key access

mysql 联接查询算法之Batched key access

作者: 尹楷楷 | 来源:发表于2020-12-07 15:27 被阅读0次

    Batched key access (BKA)

    BKA算法集结了 INLJ、BNLJ、MRR 算法的特性。它用到了INLJ的内部表索引减少关联匹配的次数;又使用到了BNLJ的join buffer,用以暂存外表连接数据减少访问内部表;还用到了MRR的收集主键rowid后排序再回表查询,随机IO转顺序IO;可以把BKA看做是INLJ算法的加强版!

    我们知道INLJ使用内部表索引进行关联能够大幅提升查询效率,但是当查询中涉及到其它字段时需要回表查询。通过辅助索引进行联接后需要回表,这里需要大量的随机I/O操作。若能优化随机I/O,那么就能极大的提升Join的性能。为此,MySQL 5.6(MariaDB 5.3)开始支持Batched Key Access Join算法(简称BKA),该算法通过常见的空间换时间,随机I/O转顺序I/O,以此来极大的提升Join的性能。

    在说明Batched Key Access Join前,首先介绍下MySQL 5.6的新特性mrr——multi range read。
    这篇有对MRR进行说明 https://www.jianshu.com/p/3d9b9b4ea186

    理解了 MRR 性能提升的原理,我们就能理解 MySQL 在 5.6 版本后开始引入的 Batched Key Acess(BKA) 算法了。我们知道 INLJ 算法执行的逻辑是:从驱动表一行行地取出 join 条件值,再到被驱动表去做 join。也就是说,对于被驱动表来说,每次都是匹配一个值。这时,MRR 的优势就用不上了。那怎么才能一次性地多传些值给被驱动表呢?方法就是,从驱动表里一次性地多拿些行出来,一起传给被驱动表。既然如此,我们就把驱动表的数据取出来一部分,先放到一个临时内存。这个临时内存不是别人,就是 join_buffer。

    我们知道 join_buffer 在 BNL 算法里的作用,是暂存驱动表的数据。但是在 NLJ 算法里并没有用。那么,我们刚好就可以复用 join_buffer 到 BKA 算法中。NLJ 算法优化后的 BKA 算法的流程,整个过程如下所示:

    MySQL联接查询算法(NLJ、BNL、BKA、HashJoin)

    外表-->外表join buffer --> 内表index-->收集主键进行排序(mrr)--> 内表

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

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

    Batched Key Access Join算法的工作步骤如下:
    1、 将外部表中相关的列放入Join Buffer中。
    2、
    3、批量的将Key(索引键值)发送到Multi-Range Read(MRR)接口。
    4、Multi-Range Read(MRR)通过收到的Key,根据其对应的ROWID进行排序,然后再进行数据的读取操作(随机IO转顺序IO)。
    5、返回结果集给客户端。

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

    SHOW VARIABLES LIKE '%optimizer_switch%'
    

    index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on

    以下设置启用BKA:

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

    官方给出的BKA和MRR性能基准测试


    image.png

    如何确保使用到BKA并达到了优化目的?

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

    因为BKA算法的本质是通过MRR接口将非主键索引对于记录的访问,转化为根据ROWID排序的较为有序的记录获取,所以要想通过BKA算法来提高性能,不但需要确保联接的列参与match的操作(联接的列可以是唯一索引或者普通索引,但不能是主键),还要有对非主键列的search操作。例如下列SQL语句:

    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                  | 331570 |   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)
    

    列a.gender是表employees的数据,但不是通过搜索idx_birth_date索引就能得到数据,还需要回表访问主键来获取数据。因此这时可以使用BKA算法。但是如果联接不涉及针对主键进一步获取数据,内部表只参与联接判断,那么就不会启用BKA算法,因为没有必要去调用MRR接口。比如search的主键(a.emp_no),那么肯定就不需要BKA算法了,直接覆盖索引就可以返回数据了(二级索引有主键值)。

    explain select a.emp_no, 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                  | 331570 |   100.00 | NULL        |
    |  1 | SIMPLE      | a     | NULL       | ref  | idx_birth_date | idx_birth_date | 3       | employees.b.from_date |     62 |   100.00 | Using index |
    +----+-------------+-------+------------+------+----------------+----------------+---------+-----------------------+--------+----------+-------------+
    2 rows in set, 1 warning (0.00 sec)
    

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

    什么样的场景适合使用BKA join?

    加了辅助索引的字段所在表作为join的内表,索引无法覆盖,select需要回表

    使用hint

    mysql> EXPLAIN  SELECT /*+ BKA(b)*/ 
        a.gender,
        b.* 
    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                   | 300043 |   100.00 | NULL                                   |
    |  1 | SIMPLE      | b     | NULL       | ref  | `from_date`,`dept_no` | `from_date`,`dept_no` | 3       | employees.a.birth_date |     47 |   100.00 | Using join buffer (Batched Key Access) |
    +----+-------------+-------+------------+------+-----------------------+-----------------------+---------+------------------------+--------+----------+----------------------------------------+
    2 rows in set (0.03 sec)
    
    

    相关文章

      网友评论

          本文标题:mysql 联接查询算法之Batched key access

          本文链接:https://www.haomeiwen.com/subject/sblniktx.html