接触过oracle的应该知道dataguard, 而mysql中有也类似的,叫做mysql主从同步复制。搭建mysql主从复制的文章网上很多,这里就不介绍了,这里说说mysql复制的概念与原理。Mysql复制虽然常被叫mysql主从同步复制,其实真正的它不是同步的,因为它在数据同步方面是有延时的,所以mysql复制其实是异步复制。

和其它复制一样,mysql复制基本也就是用于如下几个用途:

备份用作热备类似dataguard 和九桥的同步备份

高可用 MM+keepalived mha等

业务上的读写分离

分离其它操作到备机如备份、线下统计

在介绍mysql复制之前先介绍mysql两种日志,binlog和relay-log。类比Binlog即相当于oracle里面的archivelog,而relay-log相当于oracle的standby redo log. 和oracle一样 mysql里面事务在提交之前也会先写日志,即先写binlog,如果innodb存储引擎则会先写redo log,再写binlog,redo log是innodb层面的日志,而binlog则是mysql层面的日志,不仅记录innodb存储引擎的记录,还会记录其它存储引擎的记录。而relay-log则是slave端接收master端传过来的日志。

所以一句话总结mysql复制就是master将记录在binlog里面的内容传到slave端,再应于数据库。

Binlog记录有三种格式,一种是statement 即记录语句的,优点是binlog日志量少,节省空间和IO,易于理解,有时查问题也好追寻,但是有的语句并不会被记录如一些特殊的函数;

另外一种是row语句级别的,优点是所有情况都能复制,比较可靠,缺点是日志量大;

还有就是mixed 混全模式的,先用statement模式记录binlog,如果statement记录不了再用row记录,这种一般不推荐用,因为bug比较大,有时候越强大越复杂的东西相反越不靠谱,能用简单的实现就不要搞的那么复杂。

有了上面知识,下面就可以来说说mysql复制是如何工作的:

由详解上图可知:

a.      master 将改变记录到二进制日志binary log 中,并通知slave端(dump进程)

b.      slave 上的IO线程将主库上的日志复制到自己的 relay log 中

c.      slave 上SQL线程回放中继日志的内容,使slave 上的数据与 master 达到一致

所以mysql复制中相关的进程有三个dump进程(master上),IO进程和sql进程(slave上)

另外我们知道DG有两种格式的一种是物理dataguard,即我们平时用的最多的,还有一种是logic dataguard,这个我们平时用的少。Mysql而里面是通过binlog 记录的格式来决定复制是哪种的,如果binlog是ROW格式的,即类似于oracle里面的物理复制,如果是statement格式的即类似于oracle里面的逻辑复制。

而我们为什么说mysql同步复制是异步复制呢?

首先我们来模拟一个简单的mysql事务发生过程:

master 事务一写入binlogbuffer内存,这时master 的dump进程通知slave端哥们有新事务了

在master给slave说一声之后,master进行checkpoint提交事务

slave IO进程去取相关事务内容并写入到自己的relay log

slave 的sql进程把relay log中的内容应用于本地数据库(类似于oracle进程的mrp进程应用日志),这样master中数据就全同步到slave端,主从就同步了。如果主端挂掉,那我们可以用slave来接替master继续业务。

但是其实上面说的都是理想状态,比如现在如果master告知slave有新事务了,但是网络出现故障了,slave 端还没来得急接收事务,而此时master挂掉了,那我们岂不是丢了一部分数据,当然我们这里说的只是其中一种情况。说mysql主从复制是异常复制的最大原因是因为他有延时,而延时的原因除了刚刚我们比喻的这种因为网络原因影响IO进程外,还有就是如果磁盘速度跟不上会影响sql进程,其实最大的原因是因为master端事务提交是多线程的的并行操作,而slave端则是单事务的串行化操作。

所以为了解决mysql复制的同步一致性问题,mysql5.5开始推出了半同步复制,同样我们还是用事务流程来说明:

master 事务一写入binlogbuffer内存,这时master 的dump进程通知slave端哥们有新事务了

在master给slave说一声之后,些master并不急着提交事务,而是发送一个ack(相当于消息)给slave ,master等待checkpoint提交事务

Slave接收完事务后反馈一个ack给master

Master接收到一个反馈ack 后进行checkpoint提交事务

Slave的 IO进程接收相关事务内容并写入到自己的relay log

slave 的sql进程把relay log中的内容应用到数据库并写入到本地硬盘

这里我们说它是半同步复制,是因为如果slave端没有接收到这个反馈ack的话,它就会变为普通的异常复制。

新的东西也会有它不好的一面,半同步复制也会带来新的问题:

如果异常发生,就会成为普通的复制,所以只能减少数据发生不一致性的概率

Master端dumper线程要干的活多了,发送ack和接收反馈ack,性能肯定受到影响

这里的master接收到slave端的反馈ack时,也只是将事务内容发生到slave到的内存中,并不保障写入到从库的硬盘中,因此如果此时master挂掉了,主从还是有不一致的可能。

不过这一切都在mysql5.7中得到非常大的改善mysql5.7中增加了rpl_semi_sync_master_wait_point参数, 该参数有两个值after_commit和after_sync.

Alfter_commit是5.6中默认的,即事务写只写到slave内存中master就会提交事务:

而5.7之后该参数默认的是after_sync,只有事务真正写入到slave端的relaylog硬盘中后,master这边才会把事务提交到存储引擎中去。

同时5.7中还推出了一个进程ack collect thread线程,替换了dump进程进行ack的发送与接收工作,这样极大的解放了dump线程大大提高了性能。

到现在为止,我们上面说的几个问题就只有最后一个问题没有解决了,那就是并行相关的问题,即主端master是事务的并行提交,而slave端才是单事务的串行化操作。好在对于此mysql从5.6就开始着手解决了,5.6开始推出了一个叫并行复制的东西,不过5.6中的并行复制并不是真正的并行复制,因为它是基本database的,即基于库的,而到了5.7中则引入了一种行的并行模式,它的思想就是为同时提交的事务分配相同的序列号,让他们一起提交,

5.7称之为组提交group commit, 关于groupcommit这里就不详细讲了,因为篇幅有限,没有几页ppt讲不完的,同样5.7推出的mgr也是基于并行复制和group commit的。具体实现MySQL 5.7则是引入了一个新的变量slave-parallel-type,其可以配置的值有: 1. DATABASE (5.7之前默认值),基于库的并行复制方式; 2. LOGICAL_CLOCK (5.7新增值),基于组提交的并行复制方式。

最终mysql认为这些具有相同序列号的事务在master端可以一起提交,同样在slave端也可以一起提交。

以上简单的介绍了一下mysql复制的发展进步与改进过程,所以mysql复制说就是传统类型的异常复制,而真正的mysql同步则是最近刚出来mgr pxc,其中我们华泰证券做的就是galera cluster.

对于传统的复制如果不考虑开源等因素的话,在5.7发布之前同步一致性做的最好的是mariadb的复制,基本上能做到主从复制的零延时。而5.7增强半同步的的引入则大大提高了官方版本的同步效率,所以此时更推荐5.7的同步。

当然未来趋势,个人更看好Galera cluster 和Mysql Group replication 的发展,这也是官方大力推荐的,它们真正地解决了多点写入的问题,不过在多点写入的性能方面还有待提高,另外同前同步技术发展不是很成熟,有很多限制,不建议在生产上用。相比而言,oracle的rac还是很牛逼的,值得mysql学习与引进。

另外复制技术的发展影响更多的也就是高可用架构的选用,在5.7中随着GTID的成熟,基本上很多业务都可以采用MM/MS GTID+row的模式就可能实现,80%左右的业务场景都可以满足,同样它还能替代mha等高可用架构。