博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[MySQL 5.6] 5.6新参数slave_rows_search_algorithms
阅读量:6207 次
发布时间:2019-06-21

本文共 3907 字,大约阅读时间需要 13 分钟。

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

在MySQL5.6中提供了一个新的参数:, 可以部分解决无主键表导致的复制延迟问题,其基本思路是对于在一个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;1108911090       case ROW_LOOKUP_INDEX_SCAN:11091         do_apply_row_ptr= &Rows_log_event::do_index_scan_and_update;11092         break;1109311094       case ROW_LOOKUP_TABLE_SCAN:11095         do_apply_row_ptr= &Rows_log_event::do_table_scan_and_update;11096         break;1109711098       case ROW_LOOKUP_NOT_NEEDED:11099         DBUG_ASSERT(get_general_type_code() == WRITE_ROWS_EVENT);1110011101         /* No need to scan for rows, just apply it */11102         do_apply_row_ptr= &Rows_log_event::do_apply_row;11103         break;1110411105       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退出

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

转载地址:http://thhca.baihongyu.com/

你可能感兴趣的文章
thinkphp实现分页
查看>>
自动化运维之saltstack(二)states深入理解
查看>>
变量拼接的四种方法
查看>>
expect 批量修改服务器用户密码
查看>>
linux下配置SS5(SOCK5)代理服务
查看>>
Nginx平滑升级到最新版本
查看>>
Python自动化运维:Django之View视图和Template
查看>>
hive配置
查看>>
Oracle Controlfile控制文件中记录的信息片段sections
查看>>
菜鸟也学hadoop(1)_搭建单节点的hadoop
查看>>
spanning-tree extend system-id
查看>>
思维导图分析http之http协议版本
查看>>
linux延时与定时操作
查看>>
JavaScript语法详解(三)
查看>>
老王学linux-ftp
查看>>
“换标”Intel的穷则思变
查看>>
一个APP的由来
查看>>
DNS 原理
查看>>
Centos 安装 禅道
查看>>
Python 学习日记第二篇 -- 列表,元组
查看>>