MySQL的主从复制是将一台MySQL主机(master)的数据复制到其它MySQL主机(slave)中。复制过程中主服务器将更新写入二进制文件,并维护文件的一个索引以跟踪日志循环。这些日志记录发送到从服务器的所需要的更新,当一个从服务器连接主服务器时,它通知主服务器在服务器日志中读取的最后一次成功更新的位置,从服务器就接收上次更新位置之后的所有更新。
注意:所有从服务器的更新都应从主服务器获取,不然会产生数据不一致的情况。
MySQL的复制分为3种:
一、MySQL的异步复制
- 正常的复制:事务(t1)写入binlog buffer;dumper线程通知slave有新的事务t1;binlog buffer进行checkpoint;slave的IO线程收到事务t1并写到自己的relay log中;slave的SQL线程读relay log并重放日志,更新本地数据库。这时,master和slave都能看到这条事务,即使master挂了,slave也可以接管成为master。
1. 一个MySQL服务器作为master,master在每个事物更新数据完成前将更新的动作串行的写入到二进制文件(binarylog)中,事件写入完成之后,master将通知存储引擎提交事务;
2. slave将master的binarylog文件拷贝到它自己的中继日志。首先,slave开始一个工作线程——I/O线程,I/O线程在master上打开一个连接,然后master执行dump线程,binary dump process从master的二进制文件中读取事件,如果已经跟上master,它会睡眠等待新的master事件。I/O线程将这些事件写入到中继事件(relay log,一般位于缓存中)中。
3. SQL thead线程将读取relay log,并重放其中的事件,从而更新数据,使其保持与master数据一致。
异常的复制:事务(t1)写入binary buffer;dumper线程通知slave有新的事务t1;binlog buffer进行checkpoint;slave因网络不稳定,没有收到t1,然而此时master挂掉,slave成为新的master,事务t1丢失。
其它问题:复制过程有一个限制——复制在slave上是串行的,也就是说master上的并行更新操作不能再slave上并行操作。master节点的多个数据库并发进行事务提交,提交的事务根据LSN号顺序的写入binlog,slave节点通过I/O线程将master上的binlog写到本地relaylog中,在slave节点只有一个SQL线程来执行relaylog中的日志,这样很容易造成slave延迟主机和从机之间同步存在延迟。当业务并发上来时,slave因为要顺序执行master批量事务,导致很大的延迟。
为了弥补以上几种场景的不足,MySQL5.5开始推出了半同步:
即在master的dumper线程通知slave后,同时主库提交事物,并增加一个ack,即slave是否成功接收到t1的标志码。也就是dumper线程除了发送t1还需接收ack回复。只有收到ack回复后才给客户端返回OK。如果出现异常,那么自动降为异步复制,直到异常恢复。
但是半同步仍然有不足之处:
-
- 如果发生异常,会降级为异步复制。虽然从机出现数据不一致的几率会减少,但不能完全消失。
主机dumper线程承担的工作多了:发送日志和接收确认,并且这两个任务是串行的,dump必须在收到ack后才能发送下一个事务。这会降低整个数据库的性能,成为瓶颈之处。
在MySQL5.5和5.6使用after_commit模式下,即如果slave没有收到事物,也就是还没有写入到relay log之前,网络出现异常,此时master挂掉,从机成为master,此时就少一个事务的数据,出现数据丢失的情况。
在MySQL5.7中的优化:
- 主从一致性加强,支持在事务commit前等待ack。即在master的dumper线程通知slave后,只有收到ack回复后主库才提交事务,然后才给客户端返回OK。这样保证了数据的同步。
- 性能提升,支持发送binlog和接受ack的异步化:主库独立出一个线程专门用于接收slave的ack。这样master上有两个线程独立工作,可以同时发送和接收。
- 性能提升,控制主库接受slave写事务成功反馈数量:MySQL5.7新增了rpl_semi_sync_master_wait_slave_count参数,可以用来控制主库接受多少个slave写事务成功反馈,给高可用架构提供了灵活性。
- 性能提升,binlog互斥锁改进:旧版本半同步复制在主提交binlog的写会话和dump thread读binlog的操作都会对binlog添加互斥锁,导致binlog文件的读写是串行化的,存在并发度的问题;
在MySQL5.7中:移除了dump thread对binlog的互斥锁;加入了安全边际保证binlog的读安全。
性能提升,组提交:
5.7引入了新的变量slave-parallel-type,其可以配置的值有:- DATABASE (5.7之前默认值),基于库的并行复制方式;LOGICAL_CLOCK (5.7新增值),基于组提交的并行复制方式;
- MySQL 5.6版本也支持所谓的并行复制,但是其并行只是基于DATABASE的,也就是基于库的。如果用户的MySQL数据库实例中存在多个DATABASE ,对于从机复制的速度的确可以有比较大的帮助,如果用户实例仅有一个库,那么就无法实现并行回放,甚至性能会比原来的单线程更差。
MySQL5.7中增加了一种新的并行模式:为同时进入commit阶段的事务分配相同的序列号,这些拥有相同序列号的事务在备库是可以并发执行的。
MySQL 5.7真正实现的并行复制,这其中最为主要的原因就是slave服务器的回放与主机是一致的即master服务器上是怎么并行执行的slave上就怎样进行并行回放。不再有库的并行复制限制,对于二进制日志格式也无特殊的要求(基于库的并行复制也没有要求)。
因此下面的序列中可以并发的序列为(其中前面一个数字为last_committed ,后面一个数字为sequence_number ):
备库并行规则:当分发一个事务时,其last_committed 序列号比当前正在执行的事务的最小sequence_number要小时,则允许执行。
因此,- trx1执行,last_commit<2的可并发,trx2, trx3可继续分发执行
- trx1执行完成后,last_commit < 3的可以执行, trx4可分发
- trx2执行完成后,last_commit < 4的可以执行, trx5, trx6可分发
- trx3、trx4、trx5完成后,last_commit < 7的可以执行,trx7可分发