[MySQL] 轻松读懂MySQL复制演进过程

(1)前言

    

          mysql一直以来的主从复制都是被诟病的理由之一,原因是:

        1. 主从复制速度问题(最主要的)

        mysql采取的是主执行提交之后,将语句记录进binlog,从启动一个IO线程从主传输binlog到本地,进入本地的relaylog;从另外启动一个SQL线程负责顺序执行relaylog中的语句,对修改在从机上重做,即是一个异步的拷贝。导致的问题是从机数据大部分情况下会延后。主机对本地磁盘的IO,从机从主机传输的网络IO,从机本地的磁盘IO,最后从机数据的IO,经过了四个过程,即使服务器和网络配置都很快,理论上也一定是有延迟的;如果服务器和网络配置有瓶颈,这个延迟就会扩大化到影响业务的程度,最直接的就是读写分离的情况下,导致前端写入操作明明已经回报完成了,后端读数据时却显示没有完成。

        2. 主机宕机切换后脏数据问题

        mysql的主从目的之一,就是在主宕机的情况下,能及时切换到备机继续提供服务,不至于整个系统挂掉。但就是因为主从复制要经过复制这一消耗IO的步骤,主在挂掉的一瞬间,一般主从都会有一定量的数据区别,出现在主执行完的但还未传输到从上的部分,这部分就是所谓的脏数据,即之前明明已经有了的数据,又没有了。这样的脏数据,同样会导致前后不一致,如果服务器和网络配置不高,本来同步复制就慢的情况下,会导致极大的差别。


        2009年oracle公司收购了sun,对mysql进行升级换代之后,有了巨大的改进,在5.5、5.6、5.7版本都针对主从有升级改造,在目前的5.7版本达到了非常高的可用性,配合最新的HA中间件mysql fabric,可以达到以前的几倍的稳定性。下面来详细讲一讲这三个版本中,oracle都对主从具体做了什么改进。


(2)MySQL5.5版本

1. 5.5版本添加了一个semi-sync replication(半同步复制)的插件,这个插件就是为了优化同步复制的脏数据问题而生的。


2. 半同步复制在提交过程中增加了一个延迟,在提交事务后,只有在备库收到了该事务的二进制日志时才会给客户端进行查询结束的反馈。这会给客户端查询体验增加一些延迟,不过问题不大,因为相对于写入硬盘的时间,通过网络传输些日志的时延不算什么。重要的是,半同步复制不会阻塞事务的提交,而是阻塞给客户端的反馈;当备库一直没有回应已收到事件主库会超时并转化成正常的异步复制模式。


3. 顺带提一下半同步复制简单原理:在传输binlog时,要求从机返回ack证明自己拿到了数据,在至少一个从机返回ack后,主机才将数据修改提交到本地,这样能保证至少一个从机和主机是完全一致的。

image.png

4. 再举个例吧:正在主机上持续不断**数据时,始终有一部分数据处在传输到从机的过程中,这部分数据在主机上已经是执行成功的状态,但因为还没传输并完成收到任何一台从机的回应,所以尚未持久化。这个瞬间主机宕机了,数据传输不完,从机的回应也收不到了,主机重启后,主机上的这部分数据因为没有收到回应进行持久化,所以消失了。而从机上的这部分数据因为尚未接收完全,也不能作为一个relaylog提交执行,所以从机也没有这部分数据。最终的结果就是,这部分脏数据就被丢弃了,不会造成主从的不一致。

5. 优点:保证至少一个从机和主机在任何时候都是一致的,不会出现你比我多或者我比你多的情况,不需要进行主从恢复后的数据恢复行为。在只有一主一从的情况下,整个系统永远不会有脏数据。

6. 缺点:显而易见的,一主多从的情况下,除了一台从外,其他从都会有数据不一致现象。另外,没有成功传输的数据就被直接丢弃了,找都找不回来,如果涉及金融交易,瞬时的数据丢失也是不可原谅的,所以这种架构在数据重要度非常高的业务里不能用。


(3)MySQL5.6版本


1. 5.6版本对主从同步进行了改进,加入了GTID(5.6.2开始,5.6.10完善)的事务区分标志,又加入了多线程复制和组提交的新模式。


2. GTID:在MySQL5.6以前对于主从复制出现问题有时候需要分析BINLOG找到POS点,然后再CHANG MASTER TO。容易犯错,造成主从复制错误。引入GTID后,不需要再寻找BINLOG和POS点,只需要知道MASTER的IP、密码、端口就可以,MySQL会从内部GTID机制自动找到同步点。加入GTID后,一是可以根据GTID可以知道事务最初是在哪个实例上提交的,二是GTID的存在也方便了Replication的Failover。

3. GTID原理:分成两部分,一部分是服务的UUID,保存在mysql数据目录的auto.cnf文件中,这是一个非常重要的文件,不能删除,这一部分是不会变的。另外一部分就是事务ID(TID)了,随着事务的增加,其值依次递增。


4. 多线程并发复制:在MySQL5.6之前,复制是单线程队列式的,只能一个一个运行。在新版中支持基于库的多线程复制,但是库里的表不能多线程。针对多个库的情况,开启多线程,每个库一个独立IO线程,可以交叉并发传输。在不同库同一时间进行的操作理论上是互不影响的,可以同步进行,你改你的,我改我的。极大改善了多库业务的复制速度。贴个图。

image.png

5. 5.6的并行复制框架实际包含了一个协调线程和若干个工作线程,协调线程负责分发和解决冲突,工作线程只负责执行。


6. 但是多线程并发复制也不一定完全有效,其并行只是基于schema的,也就是基于库的。如果用户的MySQL数据库实例中存在多个schema,对于从机复制的速度的确可以有比较大的帮助。但如果业务始终只有一个库,或者数据库压力都集中在一个库上,其他库基本没什么操作,那针对库的多线程基本没有意义,还是和以前的单线程复制是一样的速度。所以说MySQL 5.6所谓的并行复制对真正用户来说,有点雷声大雨点小。


7. 组提交:极大提升了binlog和innodb的redolog的落盘(保存到磁盘)效率,可将多次磁盘IO可以合并成一次磁盘IO,减少读写次数,有效提高了写日志的速度。也就意味着在主从同步过程中,磁盘IO这一块的效率提高了。


8. 组提交的原理:多个事务同时执行完成,数据需要持久化到log中时,会全部进入一个待提交队列。最先进入队列的事务线程成为leader线程,其他后续进入的成为follower线程,leader线程将会获得这个队列的控制权,就是会获得一个锁,全权负责本次队列中所有事务的落盘操作。接着联系其他follower线程,将他们的提交内容获取得到,并让他们等待自己完成操作。接着这个leader线程会进入后续的落盘过程,等完成后,会通知本队列中所有follower线程落盘已经完成,可以返回成功状态了。


(4)MySQL5.7版本


1. MySQL 5.6基于库的并行复制出来后,基本无人问津,在沉寂了一段时间之后,MySQL 5.7出来了,它的并行复制以一种全新的姿态出现在用户面前。MySQL 5.7才可称为真正的并行复制,这其中最为主要的原因就是slave服务器的回放与master是一致的,即master服务器上是怎么并行执行的,那么slave上就怎样进行并行回放。不再有库的并行复制限制,对于二进制日志格式也无特殊的要求(基于库的并行复制也没有要求)。


2. 5.7之所以被看作mysql主从复制上一个划时代的版本,主要原因是mysql将日志组提交的模式应用到了主从网络IO上,在原来已经通过组提交优化了的磁盘IO效率基础上,对网络IO效率进行了同样的优化,正是将困扰mysql主从复制多年的两大难题解决的最重要版本。解决方案,官方称之为Enhanced Multi-Threaded Slave(简称MTS)。


3. 点一下MTS:增强的多线程复制,即通过从机开启多个IO线程,主机通过组提交的模式并行地传输事务进行复制。具体思想简单易懂,一言以蔽之:一个组提交的事务都是可以并行回放的,若这些事务都已进入到事务的prepare阶段,则说明事务之间没有任何冲突(否则就不可能提交),关于MTS的详细原理和实现,可以查阅资料详细学习一下,这里点到为止。


4. 但是前面提到的网络IO是有先后顺序的,和传输顺序通常是不同的,收到事务后需要区分先后顺序来执行,否则从机执行顺序和主机不同,数据同样会出错。获取事务先后顺序的方法也很简单,就是使用的MYSQL 5.6已经加入的GTID,因为每个事务在提交时已经按顺序生成了唯一的GTID,从机读到事务后按照GTID的顺序执行就可以了。


5. 在这样的模式下,从机可以同时消费主机的多个事务队列,在同一时间接收到更多的数据传输,有效降低了数据来不及传输而导致的宕机数据丢失的概率。


6.最后提一个问:MySQL5.7是如何识别哪些事务是要一起提交的呢?

    其实就是在GTID event 中增加了两个字段:int64 last committed和int64 sequence number,同一个组提交里多个事务gtid不同,但last committed却是一致的,同时一个组里的last committed对应上一个事务的sequence number。当slave的coordinator线程在分发这些event的时候,具有相同last committed 的事务(event的集合)就可以同时发送给不同的work线程,达到并行同步的目的。


总结一下,MySQL复制演进历程大致可以概括为:从5.5的单线程复制,到5.6基于Schema级别的并行复制,再到5.7最大化还原主库的支持事务级别的并行复制,其实总体来看其发展是有连贯的前因后果的,大致了解MySQL复制的来龙去脉之后,再去抠细节会清晰许多。