深度解析MySQL并行复制(MTS)原理

点击上方"数据与人", 右上角选择“设为星标”分享干货,共同成长!   

在MySQL 5.7版本,官方称为enhanced multi-threaded slave(简称MTS),就是:master基于组提交(group commit)来实现的并发事务分组,再由slave通过SQL thread将一个组提交内的事务分发到各worker线程,实现并行应用。

MySQL 5.7并行复制原理

MySQL 5.6基于库的并行复制出来后,基本无人问津,MySQL 5.7才可称为真正的并行复制,这其中最为主要的原因就是slave服务器的回放与master是一致的,即master服务器上是怎么并行执行的,那么slave上就怎样进行并行回放。不再有库的并行复制限制,对于二进制日志格式也无特殊的要求(基于库的并行复制也没有要求)。

从MySQL官方来看,其并行复制的原本计划是支持表级的并行复制和行级的并行复制,行级的并行复制通过解析ROW格式的二进制日志的方式来完成。

该并行复制的思想最早是由MariaDB的Kristain提出,并已在MariaDB 10中出现,相信很多选择MariaDB的小伙伴最为看重的功能之一就是并行复制。MTS实现了事务的并行,从某种程度来说也实现了行的并行(事务对行处理)。

下面来看看MySQL 5.7中的并行复制究竟是如何实现的?

order commit (group commit) -> logical clock ->> MTS


Master

组提交(group commit)

组提交(group commit):通过对事务进行分组,优化减少了生成二进制日志所需的操作数。当事务同时提交时,它们将在单个操作中写入到二进制日志中。如果事务能同时提交成功,那么它们就不会共享任何锁,这意味着它们没有冲突,因此可以在Slave上并行执行。所以通过在主机上的二进制日志中添加组提交信息,这些Slave可以并行地安全地运行事务。

首先,MySQL 5.7的并行复制基于一个前提,即所有已经处于prepare阶段的事务,都是可以并行提交的。这些当然也可以在从库中并行提交,因为处理这个阶段的事务,都是没有冲突的,该获取的资源都已经获取了。反过来说,如果有冲突,则后来的会等已经获取资源的事务完成之后才能继续,故而不会进入prepare阶段。这是一种新的并行复制思路,完全摆脱了原来一直致力于为了防止冲突而做的分发算法,等待策略等复杂的而又效率底下的工作。

MySQL 5.7并行复制的思想一言以蔽之:一个组提交(group commit)的事务都是可以并行回放,因为这些事务都已进入到事务的prepare阶段,则说明事务之间没有任何冲突(否则就不可能提交)。

根据以上描述,这里的重点是:

  • 如何来定义哪些事务是处于prepare阶段的?

  • 在生成的Binlog内容中该如何告诉Slave哪些事务是可以并行复制的?为了兼容MySQL 5.6基于库的并行复制,5.7引入了新的变量slave-parallel-type,其可以配置的值有:DATABASE(默认值,基于库的并行复制方式),LOGICAL_CLOCK(基于组提交的并行复制方式)

支持并行复制的GTID

那么如何知道事务是否在同一组中?原版的MySQL并没有提供这样的信息。在MySQL 5.7版本中,其设计方式是将组提交的信息存放在GTID中。

那么如果参数gtid_mode设置为OFF,用户没有开启GTID功能呢?MySQL 5.7又引入了称之为Anonymous_Gtid(ANONYMOUS_GTID_LOG_EVENT)的二进制日志event类型,

如:

mysql> SHOW BINLOG EVENTS in 'mysql-bin.000006';<br>+------------------+-----+----------------+-----------+-------------+-----------------------------------------------+<br>| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |<br>+------------------+-----+----------------+-----------+-------------+-----------------------------------------------+<br>| mysql-bin.000006 | 4 | Format_desc | 88 | 123 | Server ver: 5.7.7-rc-debug-log, Binlog ver: 4|<br>| mysql-bin.000006 | 123 | Previous_gtids | 88 | 194 | |<br>| mysql-bin.000006 | 194 | Anonymous_Gtid | 88 | 259 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |<br>| mysql-bin.000006 | 259 | Query | 88 | 330 | BEGIN |<br>| mysql-bin.000006 | 330 | Table_map | 88 | 373 | table_id: 108 (aaa.t) |<br>| mysql-bin.000006 | 373 | Write_rows | 88 | 413 | table_id: 108 flags: STMT_END_F |<br>......

用于表示上一个binlog最后一个gitd的位置,每个binlog只有一个,当没有开启GTID时此事件为空。