[MySQL 5.6] 5.6新参数slave_rows_search_algorithms

我们知道,MySQL有一个老问题,当表上无主键时,那么对于在该表上做的DML,如果是以ROW模式复制,则每一个行记录前镜像在备库都可能产生一次全表扫描(或者二级索引扫描),大多数情况下,这种开销都是非常不可接受的,并且产生大量的延迟。


在MySQL5.6中提供了一个新的参数:slave_rows_search_algorithms, 可以部分解决无主键表导致的复制延迟问题,其基本思路是对于在一个ROWS EVENT中的所有前镜像收集起来,然后在一次扫描全表时,判断HASH中的每一条记录进行更新。


测试:


首先来看看性能怎么样,我们使用sbtest表,并将其上面的主键及二级索引全部删除,表中有200万行数据

主库执行随机更新操作:

     update table sbtest2 set k = k +15 order by rand() limit  [$RECOR_NUM]; 

备库的最大延迟时间(基本等同执行时间)如下:

随机更新记录数

TABLE_SCAN,INDEX_SCAN

TABLE_SCAN,INDEX_SCAN,HASH_SCAN

10

26s

6s

20

51s

9s

40

96s

15s

60

147s

25s

80

187s

30s

100

228s

37s

可以看出来,该特性对于无主键表的复制延迟问题,还是有很大的帮助的。

如何使用:

slave_rows_search_algorithms的文档描述的非常清晰,该变量由三个值的组合组成:TABLE_SCAN,INDEX_SCAN, HASH_SCAN,使用组合包括:

TABLE_SCAN,INDEX_SCAN  (默认配置,表示如果有索引就用索引,否则使用全表扫描)

INDEX_SCAN,HASH_SCAN

TABLE_SCAN,HASH_SCAN

TABLE_SCAN,INDEX_SCAN,HASH_SCAN(等价于INDEX_SCAN, HASH_SCAN)

参数组合(摘自log_event.cc: 9633~9648)

  /*
    Decision table:
    - I  --> Index scan / search
    - T  --> Table scan
    - Hi --> Hash over index
    - Ht --> Hash over the entire table

    |--------------+-----------+------+------+------|
    | Index\Option | I , T , H | I, T | I, H | T, H |
    |--------------+-----------+------+------+------|
    | PK / UK      | I         | I    | I    | Hi   |
    | K            | Hi        | I    | Hi   | Hi   |
    | No Index     | Ht        | T    | Ht   | Ht   |
    |--------------+-----------+------+------+------|

  */

实现:

a.决定使用哪种scan方式:

函数:Rows_log_event::decide_row_lookup_algorithm_and_key

调用backtrace:

Rows_log_event::do_apply_event

     ->do_before_row_operations

          ->Rows_log_event::row_operations_scan_and_key_setup

                    ->Rows_log_event::decide_row_lookup_algorithm_and_key 

没啥好说的,根据上述的参数组合矩阵,来确定使用哪种scan策略,存储在rows log event对象的m_rows_lookup_algorithm中

另外这里也会去看看是否能够使用索引,以及使用哪个索引(this->m_key_index= search_key_in_table),

b.执行过程

在函数Rows_log_event::do_apply_event中,当确定了使用哪种scan策略后,就可以选择对应的接口函数:

11084     switch (m_rows_lookup_algorithm)
11085     {
11086       case ROW_LOOKUP_HASH_SCAN:
11087         do_apply_row_ptr= &Rows_log_event::do_hash_scan_and_update;
11088         break;
11089
11090       case ROW_LOOKUP_INDEX_SCAN:
11091         do_apply_row_ptr= &Rows_log_event::do_index_scan_and_update;
11092         break;
11093
11094       case ROW_LOOKUP_TABLE_SCAN:
11095         do_apply_row_ptr= &Rows_log_event::do_table_scan_and_update;
11096         break;
11097
11098       case ROW_LOOKUP_NOT_NEEDED:
11099         DBUG_ASSERT(get_general_type_code() == WRITE_ROWS_EVENT);
11100
11101         /* No need to scan for rows, just apply it */
11102         do_apply_row_ptr= &Rows_log_event::do_apply_row;
11103         break;
11104
11105       default:
11106         DBUG_ASSERT(0);
11107         error= 1;
11108         goto AFTER_MAIN_EXEC_ROW_LOOP;
11109         break;
11110     }

这里我们只关心Rows_log_event::do_hash_scan_and_update,主要做几件事

1.将当前行事件的记录前镜像和后镜像存储到一个hash中(Rows_log_event::do_hash_row);

hash表的结构定义在Hash_slave_rows类中

>>hash key的生成基于record[0],也就是前镜像;

>>前镜像记录的起始位置

>>前镜像记录的结束位置

对于hash scan,当有索引时(m_key_index<MAX_KEY),处理方式略微有些不一样:

在将记录加入到hash时,键值列被单独存储下来,存储在m_distinct_key_list中(Rows_log_event::add_key_to_distinct_keyset)

2.如果该EVENT中所有的行记录都解析完毕,开始执行SCAN && UPDATE (Rows_log_event::do_scan_and_update)

>>初始化表或者索引扫描(open_record_scan())

     对于索引,需要根据m_distinct_key_list初始化需要查询的键值iterator(m_itr)

>>在一个while循环中:

     |–>error= next_record_scan(i == 0);  //读取表或索引上的下一条记录

          |–>对于全表扫描,直接调用table->file->ha_rnd_next(table->record[0])扫描表记录

          |–>对于索引,第一次读是根据key搜索记录,下一次再调用next_record_scan时,先看下一条记录是否和当前m_key是否匹配,如果不匹配,将 m_key指向下一个m_itr,并根据m_key重读index,否则使用这个记录;

          这里看起来似乎有优化的余地,因为m_itr并不是有序的,它的成员取自m_distinct_key_list,并且也不会保证存储的Key完全不重复,从函数 add_key_to_distinct_keyset的逻辑可以看出来,每次加入一个新的key,仅仅和上一次的key做对比来判断是否重复,但实际上二级索引key在一个rows log event中可能是无序的,做一次排序 会不会有利于性能呢?(TODO)

     

     |–>根据读取到的记录构建key,查找hash,查看是否存在对应的entry(entry= m_hash.get(table, &m_cols))

     |–>如果存在entry,还需要把读取到的记录跟该entry对应的binlog中的行记录进行比较(防止hash collision),符合的话,则从hash中将其删除,并应用该更新(Rows_log_event::do_apply_row)

当发生错误,或者hash中记录删光后,结束扫描,从while退出

可见,对于没有索引的表而言,最多需要一次表扫描。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值