MySQL 5.7的并行复制在组提交的基础上设计的,策略就是:
- 在Master上的并行事务,在Slave上就可以并行回放
在组提交时(ordered_commit),进入Flush Stage的队列中的THD如图
其中:
- last_committed标识为同一队列中
- sequence_number标识同一队列中的先后顺序
MySQL 5.7里为每一个事务在Server层新增一个Transaction_ctx对象表示
last_committed
在语句提交时(参考MySQL 5.6:事务模型)
【Binlog Prepare】binlog_prepare修改为
sequence_number
last_committed / sequence_number的关系
- 全局变量last_committed初始化为1
- 一些事务(比如三个事务T1、T2、T3)在Prepare阶段获得了last_committed=1,并且sequence_number(T1)=1,sequence_number(T2)=2,sequence_number(T3)=3
- 这三个事务组提交成功,更新last_committed=3
- 一些事务(比如T4、T5)在在Prepare阶段获得了last_committed=3,并且sequence_number(T1)=4,sequence_number(T2)=5
- 这两个事务组提交成功,更新last_committed=5
- …
last_committed是组提交的计数
sequence_number是已经提交的事务的计数
Slave上如何回放?
两个命题:
- 【命题1】具有相同last_committed的事务肯定是并发事务
- 【命题2】具有不同last_committed的事务可能是并发事务
首先,max_committed_transaction是在Commit Stage进行更新的,而且是无锁保护的更新
【命题1证明】
假设两个事物T1、T2具有相同的last_committed=1,但不是并发事务,即一个事务的结束时间早于另一个事务的开始时间,另End_Time(T1) > Start_Time(T2)
那么:
- T1在结束前需要更新max_committed_transaction,max_committed_transaction > last_committe(T1) = 1
- T2在开始后需要获取last_committed,但max_committed_transaction > 1,因此last_committe(T2)不可能为1,矛盾
所以,具有相同last_committed的事务在Master上是并发事务,那么在Slave上也可以是并发事务(即并行回放)
【命题2证明】
考虑如下场景:
- 队列共有三个事务T1(Leader)、T2、T3刚刚进入Commit Stage,last_committed都为1
- 事务T4在Prepare阶段获得last_committed(T4)=1
- 事务T1提交,更新max_committed_transaction=2
- 事务T5在Prepare阶段获得last_committed(T4)=2
那么,T4、T5是并发事务但具有不同的last_committed。这是此种并行复制策略下的一个待优化点,T4、T5是并发事务但在Slave上没有并行回放。也就是说,我们希望并发事务几乎都具有相同的last_committed,构成命题3
- 【命题3】并发事务几乎都具有相同的last_committed
为了使【命题3】的概率很大,实现上就是:
- 在事务刚一开始就获取last_committed(与事务开始时间越接近越好):在binlog_prepare,就获取last_committed
- 在事务即将结束才更新max_committed_transaction(与事务结束时间越接近越好):在Commit Stage,才更新max_committed_transaction
在Master上其实还有一个隐藏的设定,事务都存在于一个线程中,我们肯定是在客户端连接到MySQL后,输入类似下面的一连串命令:
这个事务中的所有SQL语句肯定是由一个线程接收到,那么在Slave上也会这样?
因为在Flush Stage时,每个线程将私有的Binlog日志提交到公共Binlog Buffer中,因此在Binlog日志文件中,事务的记录是连续的