zoukankan      html  css  js  c++  java
  • MySQL Index--BAK和MRR演示

    搭建测试环境演示BKA和MRR特性

    建表语句:

    ## 创建测试表tb1和tb2
    CREATE TABLE `tb1` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `c1` int(11) DEFAULT NULL,
      `c2` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `idx_c1` (`c1`)
    ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
    
    CREATE TABLE `tb2` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `c1` int(11) DEFAULT NULL,
      `c2` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `idx_c2` (`c2`)
    ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
    
    ## 向测试表tb1和tb2插入30万数据
    ## 表tb1001的id值自增
    INSERT INTO tb1(c1,c2) SELECT id,id FROM tb1001;
    INSERT INTO tb1(c1,c2) SELECT id,id FROM tb1001;

    测试SQL:

    SELECT * 
    FROM tb1
    INNER JOIN tb2
    ON tb1.c1 = tb2.C2
    WHERE tb1.c1>100 AND tb1.c1<200

    对应执行计划:

    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: tb1
       partitions: NULL
             type: range
    possible_keys: IDX_C1
              key: IDX_C1
          key_len: 5
              ref: NULL
             rows: 99
         filtered: 100.00
            Extra: Using index condition; Using MRR
    *************************** 2. row ***************************
               id: 1
      select_type: SIMPLE
            table: tb2
       partitions: NULL
             type: ref
    possible_keys: IDX_C2
              key: IDX_C2
          key_len: 5
              ref: demodb.tb1.C1
             rows: 1
         filtered: 100.00
            Extra: Using join buffer (Batched Key Access)
    2 rows in set, 1 warning (0.00 sec)

    对应执行计划(JSON):

    {
      "query_block": {
        "select_id": 1,
        "cost_info": {
          "query_cost": "258.44"
        },
        "nested_loop": [
          {
            "table": {
              "table_name": "tb1",
              "access_type": "range",
              "possible_keys": [
                "IDX_C1"
              ],
              "key": "IDX_C1",
              "used_key_parts": [
                "C1"
              ],
              "key_length": "5",
              "rows_examined_per_scan": 99,
              "rows_produced_per_join": 99,
              "filtered": "100.00",
              "index_condition": "(((`demodb`.`tb1`.`C1` > 100) and (`demodb`.`tb1`.`C1` < 200)) and (`demodb`.`tb1`.`C1` is not null))",
              "using_MRR": true,
              "cost_info": {
                "read_cost": "119.81",
                "eval_cost": "19.80",
                "prefix_cost": "139.61",
                "data_read_per_join": "1K"
              },
              "used_columns": [
                "ID",
                "C1",
                "C2"
              ]
            }
          },
          {
            "table": {
              "table_name": "tb2",
              "access_type": "ref",
              "possible_keys": [
                "IDX_C2"
              ],
              "key": "IDX_C2",
              "used_key_parts": [
                "C2"
              ],
              "key_length": "5",
              "ref": [
                "demodb.tb1.C1"
              ],
              "rows_examined_per_scan": 1,
              "rows_produced_per_join": 99,
              "filtered": "100.00",
              "using_join_buffer": "Batched Key Access",
              "cost_info": {
                "read_cost": "99.02",
                "eval_cost": "19.80",
                "prefix_cost": "258.44",
                "data_read_per_join": "1K"
              },
              "used_columns": [
                "ID",
                "C1",
                "C2"
              ]
            }
          }
        ]
      }
    }

    执行计划伪代码(个人理解):

    c1_condition=((`demodb`.`tb1`.`C1` > 100) 
        and (`demodb`.`tb1`.`C1` < 200) 
        and (`demodb`.`tb1`.`C1` is not null))
    
    tb1_mrr_buffer=new buffer(@@read_rnd_buffer_size)
    
    tb1_search_result=[]
    for each tb1_index_row(c1,id) in(range scan tb1.idx_c1 with c1_condition):
        if tb1_mrr_buffer is not full:
            tb1_mrr_buffer.append(tb1_index_row(c1,id))
        else## using mrr
            tb1_mrr_buffer.sort_by(id)
            for tb1_index_row(id,c1) in tb1_mrr_buffer:
                tb1_data_row(id,c1,c2)=(search index tb1.priamry_key with id=tb1_index_row.id)
                tb1_search_result.append(tb1_data_row)
                
    tb1_mrr_buffer.sort_by(id)
    for tb1_index_row(id,c1) in tb1_mrr_buffer:
        tb1_data_row(id,c1,c2)=(search index tb1.priamry_key with id=tb1_index_row.id)
        tb1_search_result.append(tb1_data_row)
    tb1_mrr_buffer.dispose()
    
    
    tb2_bka_buffer=new buffer(@@join_buffer_size)
    tb2_join_result=[]
    for each tb1_data_row(id,c1,c2) in tb1_search_result:
        for tb2_index_row(c2,id) in (range scan tb2.idx_c2 where c2=tb1_data_row.c1)
            if tb2_bka_buffer is not full:
                tb2_bka_buffer.append(tb1_data_row(id,c1,c2),tb2_index_row(c2,id))
            else## using bka
                tb2_bka_buffer.sort_by(tb2_index_row.id)
                tb2_data_row(id,c1,c2)=(search index tb2.priamry_key with id=tb2_index_row.id)
                tb2_join_result.append((tb1_data_row(id,c1,c2),tb2_index_row(c2,id)))
    tb2_bka_buffer.sort_by(tb2_index_row.id)
    tb2_data_row(id,c1,c2)=(search index tb2.priamry_key with id=tb2_index_row.id)
    tb2_join_result.append((tb1_data_row(id,c1,c2),tb2_index_row(c2,id)))        
    tb2_bka_buffer.dispose()
    
    return tb1_tb2_join_result

    PS1:MRR特性使用的buffer大小受限于参数read_rnd_buffer_size,而BKA使用的buffer大小受限于join_buffer_size

    关于BKA和MRR算法个人理解:

    1、MRR针对单表操作,将"循环索引键查找"改为"索引键缓存==>索引键排序==>索引键查找",降低随机IO操作,提升查询性能。
    2、BKA针对JOIN操作,循环"关联外表"记录对"关联内表"做MRR操作,因此BKA依赖于MRR。

    论证测试1:

    ## SQL语句
    SELECT tb1.* 
    FROM tb1
    WHERE tb1.c1>100 AND tb1.c1<200;
    
    ## 执行计划
    +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+----------------------------------+
    | id | select_type | table | partitions | type  | possible_keys | key    | key_len | ref  | rows | filtered | Extra                            |
    +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+----------------------------------+
    |  1 | SIMPLE      | tb1   | NULL       | range | IDX_C1        | IDX_C1 | 5       | NULL |   99 |   100.00 | Using index condition; Using MRR |
    +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+----------------------------------+

    上面查询通过MRR来优化tb1上对ID的随机查找。

    论证测试2:

    ## SQL语句
    SELECT tb1.* FROM tb1 INNER JOIN tb2 ON tb1.c2 = tb2.c2 WHERE tb1.c1>100 AND tb1.c1<200; ## 执行计划: +----+-------------+-------+------------+--------+---------------+---------+---------+---------------+------+----------+-----------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+---------------+---------+---------+---------------+------+----------+-----------------------------------------------+ | 1 | SIMPLE | tb1 | NULL | range | IDX_C1 | IDX_C1 | 5 | NULL | 99 | 100.00 | Using index condition; Using where; Using MRR | | 1 | SIMPLE | tb2 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | demodb.tb1.C2 | 1 | 100.00 | Using index | +----+-------------+-------+------------+--------+---------------+---------+---------+---------------+------+----------+-----------------------------------------------+

    由于SELECT子句中仅包含tb1的列,无需对tb2做回表查找,执行计划显示查询未使用BKA特性,因此推断上面测试中使用到的BKA特性主要用于优化表tb2的回表查询,而不是用于优化tb2上idx_c2上的索引查找。

  • 相关阅读:
    redis.conf
    redis 超全的操作
    Oracle数据库导入导出命令
    技巧总结
    C#把Object对象转换成JSON串
    vscode c++ 开发环境踩坑
    vscode python 开发环境+qgis开发
    算法设计与分析:贪心算法
    操作系统概念习题
    贪心算法:贪心选择性与优化子结构
  • 原文地址:https://www.cnblogs.com/gaogao67/p/12172876.html
Copyright © 2011-2022 走看看