前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MySQL5.7并行复制解析

MySQL5.7并行复制解析

作者头像
AsiaYe
发布2020-05-29 10:42:45
1.2K0
发布2020-05-29 10:42:45
举报
文章被收录于专栏:DBA随笔

//

MySQL5.7并行复制解析

//

在之前的文章中,我对MySQL并行复制做过一个简单的介绍,有兴趣可以翻看5月19日的文章《MySQL并行复制解析》。今天针对这个问题,补充一些知识点。

MySQL的并行复制,其本质是想找到互不影响的事务,好在从库上进行并行的binlog重放。MySQL5.6的并行复制是基于数据库级别的,不同数据库的事务可以同时进行binlog重放。MySQL5.7和MySQL5.6的处理方案完全不同,但是MySQL5.7中兼容了MySQL5.6的并行复制方案,用参数slave_parallel_type进行兼容,如果设置为database,则使用5.6版本的数据库级别的并行复制,如果设置为logical_clock,则是全新的并行复制方案。而slave_parallel_workers的值代表并行复制sql_thread的worker线程个数。

代码语言:javascript
复制
mysql> show variables like "%slave_para%";
+------------------------+----------+
| Variable_name          | Value    |
+------------------------+----------+
| slave_parallel_type    | DATABASE |
| slave_parallel_workers | 0        |
+------------------------+----------+
2 rows in set, 1 warning (0.00 sec)

MySQL5.7中,对于并行复制进行了大刀阔斧的改革,它的思路是所有处于redo log prepare阶段的事务,都可以并行提交,原因是这些事务都已经经过了锁资源争用的阶段,都是没有冲突的。这个结论我们可以进行反证,如果这些事务之间有冲突,则后来的事务会等待前面的事务释放锁之后才能执行,因此,这些事务就不会进入prepare阶段。

这种思路完全摆脱了老版本中致力于防止冲突而进行的事务分发算法、事务等待策略等复杂低效的工作。这种思路我们需要解决的重点问题有两个:

问题1、要找出哪些事务是同时处于redo log的prepare阶段,也就是如何进行事务分组;

问题2、如何告知slave哪些事务是可以并行的。

开始这俩问题前,首先我们需要了解MySQL5.7版本并行复制中binlog的两个参数:

last_committed

sequence_number

来看一段我截取的binlog:

代码语言:javascript
复制
#200527 21:00:27 server id 1944313  end_log_pos 615 CRC32 0xcce81a26    Xid = 945211936

COMMIT/*!*/;

# at 615

#200527 21:00:27 server id 1944313  end_log_pos 680 CRC32 0x2c85f745    Anonymous_GTID  last_committed=1        sequence_number=2

SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;

# at 680

#200527 21:00:27 server id 1944313  end_log_pos 762 CRC32 0x41f708fc    Query   thread_id=12    exec_time=0     error_code=0

上面的binlog中,可以看到last_committed=1,而sequence_number=4.

关于这两个参数,有这么几条规则:

1、last_committed相同的事务,是可以并行复制和提交的。

2、sequence_number是顺序增长的,每一个事务对应一个序列号,也就是对应一个sequence_number

3、每一组last_committed的值,都是上一个组中sequence_number的最大值,也是本组事务的最小值减一

以上三条规则,可以举个例子来看:

事务1 last_committed=0,seq_num=1

事务2 last_committed=1,seq_num=2

事务3 last_committed=1,seq_num=3

事务4 last_committed=1,seq_num=4

事务5 last_committed=4,seq_num=5

事务6 last_committed=4,seq_num=6

事务7 last_committed=6,seq_num=7

事务8 last_committed=6,seq_num=8

四种颜色,代表这8个事务可以分为4个组,每一组中的内容都可以并行提交。

下面来回答问题1MySQL如何进行事务分组(主库)?

在MySQL中,其实是通过函数来处理并行复制的,函数叫order_commit,当我们要提交事务的时候,会调用order_commit这个函数,这个函数的功能是将事务加入到队列中。而事务的提交过程,一共涉及三个队列,分别是flush队列、sync队列、以及commit队列。

下面介绍这三个队列:

1

flush队列

首先是要加入到flush队列中,flush队列中有这么几条规则:

1、如果某个事务加入flush队列中的时候,该队列是空的,那么这个事务就是当前这个队列的队长;

2、队长加入到队列中之后,有一个时间间隔t,这个时间间隔内,如果有新的事务一起加入到flush队列的话,那么队长将代替他们来执行flush动作,

3、队长将本组事务从flush队列中拿出来,准备做flush操作,此时下一组事务就可以继续加入flush队列了

5、同一时刻只能有一组事务进行flush操作.

flush操作究竟干了些什么?

1、给每一个事务分配sequence_number,如果是第一个事务,则将这个组的last_committed设置为sequence_number-1,如果不是,则按照本组第一个事务的last_committed来分配该事务的last_committed

2、将带着last_committed和sequence_number的GTID事件flush到binlog文件中

3、将当前事务所产生的的binlog内容flush到binlog文件中。

其实这个过程中,也就对事务进行了分组。

2

sync队列

接下来是sync队列,flush队列执行完毕之后,开始sync操作,sync操作前需要判断sync的缓冲区是否为空,如果为空,则直接做sync操作,否则有其他事务组在做sync操作,则要进行等待。

3

commit队列

sync队列完成之后,开始进入commit队列,commit队列其实做的是存储引擎层面的提交。这里不得不提一个参数:

binlog_order_commits,该参数会影响提交行为,如果设置为on,那么此时提交的过程就变成以每个事务队列的顺序提交了,也就是事务组的组长和组员的顺序固定。如果设置为off,则事务组的每个事务都会各自做存储引擎的提交操作。

代码语言:javascript
复制
mysql> show variables like "%order%";
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| binlog_order_commits        | ON    |
| slave_preserve_commit_order | OFF   |
+-----------------------------+-------+
2 rows in set, 1 warning (0.00 sec)

有了order_commit的过程后,问题1的理解就比较容易了。再来看问题2,slave如何知道哪些事务可以并行?

为了描述清楚,我们再拿出上面的例子:

事务1 last_committed=0,seq_num=1

事务2 last_committed=1,seq_num=2

事务3 last_committed=1,seq_num=3

事务4 last_committed=1,seq_num=4

事务5 last_committed=4,seq_num=5

事务6 last_committed=4,seq_num=6

事务7 last_committed=6,seq_num=7

事务8 last_committed=6,seq_num=8

1、从库SQL线程拿到一个新事务,取出last_committed以及seq_num值。

2、判断当前last_committed是不是大于当前已经执行的seq_num的最小值(这个值我们称为低水位,简称为LWM)。

3、如果大于,则说明上一个组的事务还没有完成,此时当前事务组要进入等待状态,知道last_committed与LWM相等(意味着上一组事务执行完毕了)才可以继续。

4、如果小于或者等于,则说明当前事务与正在执行的组是同一个组,不需要等待。

5、SQL线程经过统计,找到一个空闲的worker线程,如果没有空闲的,则SQL线程转入等待状态,知道找到一个为止。

6、将当前事务打包,交给选定的worker,之后worker线程会去应用这个事务。此时SQL线程就会处理下一个事务。

注意:我们知道事务是有时间组成的,实际过程中事务的分发还是一个一个事件的方式分发的,如果一个事务已经选择了一个worker,那么后续所有该事务的event都在这个worker上执行

简单总结一下:

1、相同last_committed的事务可以并行执行;

2、相同last_committed的事务可以在不同的worker上执行;

3、同一个事务的event必须在同一个worker上执行。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-05-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DBA随笔 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档