轻松读懂MySQL主从复制演进过程

举报
山山而川&潺潺成镜 发表于 2020/03/31 23:00:37 2020/03/31
【摘要】 今天来和大家聊聊MySQL的复制演进过程,熟悉MySQL的同学应该都知道什么是MySQL复制mysql,关于这块知识网络上也有各种材料,笔者查找过很多资料,发现很多材料都描述得不够全面,光看单篇文章难以建立整体概念,因此梳理了一版浅显全面的文章出来,帮助大家轻松读懂MySQL复制演进过程。

(1)前言

        今天来和大家聊聊MySQL的复制演进过程,熟悉MySQL的同学应该都知道什么是MySQL复制mysql,关于这块知识网络上也有各种材料,笔者查找过很多资料,发现很多材料都描述得不够全面,光看单篇文章难以建立整体概念,因此梳理了一版浅显全面的文章出来,帮助大家轻松读懂MySQL复制演进过程。

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

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

        4. 再举个例吧:当在主机上持续不断insert数据时,始终有一部分数据处在传输到备机的过程中,这部分数据在主机上已经是执行成功的状态,但因为还没传输完成收到任何一台备机的回应,所以尚未持久化。这个瞬间主机宕机了,数据传输不完,备机的回应也收不到了,主机重启后,主机上的这部分数据因为没有收到回应进行持久化,所以消失了。而备机上的这部分数据因为尚未接收完全,也不能作为一个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线程,可以交叉并发传输。在不同库同一时间进行的操作理论上是互不影响的,可以同步进行,你改你的,我改我的。极大改善了多库业务的复制速度。贴个经典的图。

        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上就怎样进行并行回放。不再有库的并行复制限制,对于binlog格式也无特殊的要求(基于库的并行复制也没有要求)。

        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复制的来龙去脉之后,再去抠细节会清晰许多。



【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。