全网最全MySQL多线程复制原理,深入浅出的进军数据库开发

本文详细解释了MySQL5.7中并行复制的ordered_commit原理,涉及事务分组、序列号分配、FLUSH、SYNC和COMMIT步骤,以及多线程复制分发和异常故障恢复中的检查点机制。
摘要由CSDN通过智能技术生成

如上图图所示,last. committed有三个值,分别是0、1. 4,这就表示当前Binlog包括三个组。也就是说,last_committed中的每个值对应于一个组的编号。last_committed为4 (1) 的有三个事务,这三个事务在5.7版本中,就被定义为可以并行复制(提交)的,而sequence_number是顺序增长的,每一个事务对应一个序列号(sequence_number)。

另外,还有一个细节可能不太容易被发现,其实每一个组的last_committed 值,都是上一个组中事务的sequence_number 最大值,也是本组中事务sequence_number 最小值减1。同时这两个值的有效作用域都在文件内,只要换一个文件(Aush binary logs),这两个值就都会从0开始计数。

那么此时,还有一个很重要的技术问题一MySQL是如何做到将这些事务分组的呢?要搞清楚这个问题,首先需要了解一下MySQL的提交方式——ordered_commit。

int MYSQL_ BIN_LOG: :ordered_ commit(THD *thd, bool all, bool skip_commit)

先看他的逻辑图,如下图所示

全网最全MySQL多线程复制原理,深入浅出的进军数据库开发

从上图中可以看到,只要事务提交(调用ordered. commit),就都会先加入队列中。而提交有三个步骤,包括FLUSH. SYNC及COMMIT,相应地也有三个队列。首先要加入的是FLUSH队列,如果某个事务加入时,队列还是空的,则这个事务就担任队长,来代表其他事务执行提交操作。而在其他事务继续加入时,就会发现此时队列已经不为空了,那么这些事务就会等待队长帮它们完成提交操作。

在上图中,事务2~6都是这种坐享其成之辈,事务1就是队长了。不过这里需要注意一点,不是说队长会一直等待要提交的事务不停地加入,而是有一个时限,只有在这个时限之内成功加入到队列的,才能帮它提交。这个时限就是从队长加入开始,到它去处理队列的时间,这个时间实际上非常小,基本上就是程序从这行到那行的一个过程,也没有刻意去等待。只要队长将这个队列中的事务取出,其他事务就可以加入这个队列了。第一个加入的还是队长,但此时必须要等待。因为此时有事务正在做FLUSH,做完FLUSH之后,其他的队长才能带着队员做FLUSH。而在同-个时刻,只能有一个组在做FLUSH。这就是上图中所示的等待事务组2和等待事务组3,此时队长会按照顺序依次做FLUSH,做FLUSH的过程中,有一些很重要的事务需要去做,如下。

  • 要保证顺序必须是提交加入到队列的顺序。

  • 如果有新的事务提交,此时队列为空,则可以加入到FLUSH队列中。不过,因为此时FLUSH临界区正在被占用,所以新事务组必须要等待。

  • 给每一个事务分配sequence_ number, 如果是第一个事务,则将这个组的last_committed设置为sequence_number-1。

  • 将带着last_committed 与sequence_number的GTID事件FLUSH到Binlog文件中。

  • 将当前事务所产生的Binlog内容FLUSH到Binlog文件中。

这样,一个事务的FLUSH就完成了。接下来,依次做完组内所有事务的FLUSH,然后做SYNC。如果SYNC的临界区是空的,则直接做SYNC操作,而如果已经有事务组在做,则必须要等待。同样地,做完FLUSH之后,FLUSH 临界区会空闲出来,那么此时在等待这个临界区的组就可以做FLUSH操作了。总而言之,每一个步骤都会有事务组在做,就像一个流水线-一样。完成-件产品需要三个工序,每个工序都可以批量来做,那么每个工序车间都不会闲着,都一直重复着相同的事情,最终每个产品都是以完全相同的顺序完成。

到COMMIT这道工序时,实际做的是存储引擎提交,参数binlog. order_ commits会影响提交行为。如果设置为ON,那么此时提交就变为串行操作了,就以队列的顺序为提交顺序。而如果设置为OFF,提交就不会在这里进行,而会在每个事务(包括队长及队员)做finish_commit (FINISH)时各自做存储引擎的提交操作。组内每个事务做finish_commit是在队长完成COMMIT工序之后进行,到步骤DONE时,便会唤醒每一个等待提交完成的事务,告诉它可以继续了,那么每个事务就会去做finish_commit。 而后,自己再去做finish_commit。

这样,一个组的事务就都按部就班地提交完成了。现在也可以知道,与这个组中同时在做提交的,最多还有另外两个事务,一个是在做FLUSH, 一个是在做SYNC。

多线程复制分发原理

=========

知道了order commit原理之后,现在应该可以很容易想到,在从库端是如何分发的。从库是以事务为单位做APPLY的,每一个 事务有一个GTID事件,从而都有一个last_ committed 及sequence_ number 值,分发原理如下。

从库SQL线程拿到一个新事务,取出last_committed及sequence_number 值。

判断当前last_committed 是不是大于当前已经执行的sequence_number 的最小值(low water mark,下面称lwm)。

如果大于,则说明上一个组的事务还没有完成。此时等待lwm变大,直到last_committed与lwm相等,才可以继续。

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

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

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

当然,上面的步骤是以事务为单位介绍的,其实实际处理中还是一个事件一个事件地分发。如果一个事务已经选定了worker,而新的event还在那个事务中,则直接交给那个worker处理即可。

从上面的分发原理来看,同时执行的都是具有相同last_comitted 值的事务,不同的只是后面的需要等前面做完了才能执行,这样的执行方式有点如下图所示。

全网最全MySQL多线程复制原理,深入浅出的进军数据库开发

可以看出,事务都是随机分配到了worker线程中,但是执行的话,必须是一行一行地执行。一行事务个数越多,并行度越高,也说明主库瞬时压力越大。

异常故障恢复

======

上面已经讲清楚了MySQL 5.7版中的并行复制原理。但是,并行总是存在一个不可避免的问题,那就是在从库并行执行的过程中,如果数据库或操作系统挂了,那么此时每个线程执行的点就都是不确定的。也就是说,顺序的Binlog在被分发出去之后,从最小位置到最大位置之间这块连续的内容之间是存在断点的。如此一来, 从库恢复之后,开始执行时就需要准确无误地还原哪些已经执行,哪些还没有执行,这是这节需要解决的问题。

下图所示的情况,就是一个正在执行并行执行的事务队列,也可以说这些事务的last_com-mitted都是相同的,并且从前到后。此外,sequence_number 是顺序增长的,也就是说图中所示的是以Binlog文件内容的顺序排列的。

全网最全MySQL多线程复制原理,深入浅出的进军数据库开发

上图所表示的就是整个队列被分配之后,在某一时刻,队列被执行的状态,假设此时从库挂了,那么再次启动之后,如何继续执行,下面就这个问题做详细描述。

MySQL为了实现对执行状态的记录,做了很多工作,首先就是维护一个队列,这个队列叫GAQ (Group Assigned Queue)。SQL线程在分配某一个事务时,首先会将这个事务加入到这个队列(队列如上图所示),之后,系统会将当前事务分发到一个线程来执行。可以想到,在某一个时刻,任务队列GAQ及每个线程的执行队列如下图所示。

全网最全MySQL多线程复制原理,深入浅出的进军数据库开发

每一个事务,在分发之后,都会有一个编号。这个编号在某一段时间内,都是相对固定的,如下图所示,每一个事务都有一个编号,编号只要被分配了,就不会再变。在事务获取编号且被所属线程执行之后,它的位置信息会被FLUSH一次,这与MySQL5.5版本中的relay_info 是类似的,用来存储从库执行的位置。但现在已经变为多线程了,那么每个线程执行到什么位置,是需要记录下来的,此时就用另一个表mysql.slave_worker_ jinfo 来存储。

每个线程在执行完-一个事务之后,会把执行位置信息写入slave_worker_info 表中。但是,GAQ队列长度是有限的,不可能一-直增长 下去(否则Checkpoint_ seqno就会一直增长下去)。所以必须要在这个队列中,找到一个位置,这个位置是GAQ的起点,并且之前的Binlog都是已经执行过的,而这个过程,就被称为做检查点。那么,多线程复制就可以归结为:检查点在GAQ中周而复始地向前推进复制位置,每个线程在不断地APPLYBinlog,并且通过Checkpoint group_ bitmap 记录已经执行的事务与最新检查点的相对位置。

其实从这个意义上讲,这里的检查点和日志检查点是一样的,其共同特性如下。

  • 有限的空间范围,必须要清出一些空间来,继续后面的工作。

  • 在最新检查点之前的位置,所有的工作都已经落地。

  • 在最新检查点之后的位置,都具有不确定性。

  • 需要不断地做,来保证操作不断地推进。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

光给面试题不给答案不是我的风格。这里面的面试题也只是凤毛麟角,还有答案的话会极大的增加文章的篇幅,减少文章的可读性

Java面试宝典2021版

最常见Java面试题解析(2021最新版)

2021企业Java面试题精选

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
21企业Java面试题精选

[外链图片转存中…(img-hFoklk4i-1712323288116)]

[外链图片转存中…(img-KYhpqdP2-1712323288116)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值