天天看点

MySQL · 特性分析 ·MySQL 5.7新特性系列四

继上三期月报:

这期我们一起来学习下mysql 5.7的并行复制。

mysql的master<->slave的部署结构,使用binlog日志保持数据的同步,全局有序的binlog在备库按照提交顺序进行回放。

由于新硬件的发展,ssd的引入和多core的cpu,master节点的并发处理能力持续提升,slave节点完全按照binlog写入顺序的单线程回放,已完全跟不上master节点的吞吐能力,导致ha切换和数据保护带来巨大的挑战。

从mysql5.5版本以后,开始引入并行复制的机制,是mysql的一个非常重要的特性。

mysql5.6开始支持以schema为维度的并行复制,即如果binlog row event操作的是不同的schema的对象,在确定没有ddl和foreign key依赖的情况下,就可以实现并行复制。

社区也有引入以表为维度或者以记录为维度的并行复制的版本,不管是schema,table或者record,都是建立在备库slave实时解析row格式的event进行判断,保证没有冲突的情况下,进行分发来实现并行。

mysql5.7的并行复制,multi-threaded slave即mts,期望最大化还原主库的并行度,实现方式是在binlog event中增加必要的信息,以便slave节点根据这些信息实现并行复制。

下面我们就来看下mysql 5.7的实现方式:

mysql 5.7的并行复制建立在group commit的基础上,所有在主库上能够完成prepared的语句表示没有数据冲突,就可以在slave节点并行复制。

我们先来回顾一下group commit的情况:

在ordered commit的过程中:

1. 由leader线程帮助flush队列中的线程完成flush binlog操作,

2. 由leader线程帮助sync队列中的线程完成sync binlog操作,

为了表示主库并行度,在binlog row event增加了如下的标识:

即在gtid_event中增加两个字段:

代码中为每一个transaction准备了如下的字段:

mysql_bin_log全局对象中维护了两个结构:

事务中的sequence_number是一个全局有序递增的数字,每个事务递增1,来源mysql_bin_log.tranaction_counter.

和gtid一对一的关系,即在flush阶段,和gtid生成的时机一致,代码参考:

事务中last_committed表示在这个commit下的事务,都是可以并行的,即没有冲突,

transaction_ctx中的last_committed在每个语句prepared的时候进行初始化,来源mysql_bin_log.max_committed_transaction

而mysql_bin_log.max_committed_transaction的更新是在group commit提交的时候进行变更。

即获取这个group commit队列中的最大的sequence_number当成当前的max_committed_transaction。

所以,这个机制可以理解成,在group commit完成之前,所有可以成功prepared的语句,没有事实上的冲突,

分配成相同的last_committed,就可以在slave节点并行复制。

例如下面时序的事务:

binlog日志片段如下:

两个insert语句在prepared的时候,没有事实上的冲突,都获取当前最大的committed number = 5.

提交的过程中,保持sequence_number生成时候的全局有序性,备库恢复的时候,这两个事务就可以并行完成。

但又如下面的case:

产生如下的顺序:

session 1和session 2语句执行不冲突,分配了相同的last_committed,

session 2提交,推高了last_committed,所以session 3的laste_committed变成了1,

最后session 1提交。

注意: 这就是mysql 5.7.3之后的改进:

在mysql 5.7.3之前,必须在一个group commit之内的事务,才能够在slave节点并行复制,但如上面的这个case。

session 1 和session 2虽然commit的时间有差,并且不在一个group commit,生成的binlog也没有连续,但事实上是可以并行恢复执行的。

所以从mysql 5.7.3之后,并行恢复,减少了group commit的依赖,但group commit仍然对并行恢复起着非常大的作用。

mysql 5.7增加了如下参数:

slave_parallel_type取值:

1. database: 默认值,兼容5.6以schema维度的并行复制

2. logical_clock: mysql 5.7基于组提交的并行复制机制

group commit delay

首先,并行复制必须建立在主库的真实负载是并行的基础上,才能使mts有机会在slave节点上完成并行复制,

其次,mysql 5.7前面讨论的实现机制,可以人工的增加group commit的delay,打包更多的事务在一起,提升slave复制的并行度。但从5.7.3开始,已经减少了group commit的依赖,

尽量减少delay参数设置对主库的影响。

合理设置如下参数;

尽量使用row格式的binlog

slave_parallel_workers 太多的线程会增加线程间同步的开销,建议4-8个slave线程,根据测试来最终确定。

如果客户端有并行度,不用刻意增加master的group commit,减少对主库的影响。

另外:

booking在使用的时候遇到的如下case:

数据库的部署结构是:master->slave1->slave2

假设,当t1,t2,t3,t4四个事务在master group commit中,那么slave1线程就可以并行执行这四个事务,

但在slave1执行的过程中,分成了两个group commit,那么在slave2节点上,并行度就降低了一倍。

booking给出的后续的解法,如果slave不多,建议都挂载在master上,如果slave过多,考虑使用binlog server,来避免这样的问题。

但其实在slave1节点上进行并行恢复的时候,保持着主库的last_committed和sequence_number不变,虽然无法保证binlog写入的顺序完全和主库一致,但可以缓解这种情况。

继续阅读