主库宕机不丢数据(Master Failover without data loss)php
半同步复制实现的关键点是Master对于事务提交过程特殊处理。目前实现半同步复制主要有两种模式,AFTER_SYNC模式和AFTER_COMMIT模式。两种方式的主要区别在因而否在存储引擎提交后等待Slave的ACK。git
下面展现了半同步复制中,binlog的提交过程。sql
1. binlog prepare (doing nothing) 2. innodb prepare (fsync) 3. binlog commit (writing to fscache) 4. binlog commit (fsync) 5. loss-less semisync wait (AFTER_SYNC) 6. innodb commit (releasing row locks, changes are visible to other users) 7. normal semisync wait (AFTER_COMMIT)
半同步复制是否能保证不丢数据?安全
咱们经过几种场景来简单分析下。网络
第一种状况:假设Master前4步binlog commit执行成功后,binlog还没来得及传递给Slave,此时Master挂了,Slave做为新Master提供服务,那么备库比主库要少一个事务(由于主库的redo 和binlog已经落盘),可是不影响用户,对于用户而言,这个事务没有成功返回,那么提交与否,用户均可以接受,用户必定会进行异常捕获而重试。less
第二种状况,假设innodb commit执行成功后,binlog还没来得及传递给Slave,此时Master挂了,此时与第一种状况同样,备库比主库少一个事务。以下图所示,在AFTER_COMMIT模式下,user1在innodb commit执行完后,其余用户能够看到该事务的更新,而切换到备库后,却发现再次读这个更新又没了,这个就发生了“幻读”,若是其余事务依赖于这个更新,则会对业务逻辑产生影响。固然这仅仅是极端状况。gitlab
AFTER_SYNC模式能够解决“幻读”问题。master在AFTER_SYNC模式下,Fsync binlog后,就开始等待Slave同步。那么在进行innodb commit后,即其它事务能看到该事务的更新时,Slave已经成功接收到binlog,即便发生切换,Slave拥有与Master一样的数据,不会发生“幻读”现象。可是对于上面描述的第一种状况,结果是同样的。性能
因此,在极端状况下,半同步复制的Master-Slave会有一个事务不一致,可是对于用户而言,因为这个事务并无成功返回给用户,因此不管事务提交与否都是能够接受的,用户有必要进行查询或重试,判读是否更新成功。或者咱们想一想,对于单机而言,若事务执行成功后,返回给用户时,网络断了,用户也是面临同样的问题,因此,这不是半同步复制的问题。对于提交返回成功的事务,版同步复制保证Master-Slave必定是一致的,从这个角度来看,半同步复制不会丢数据,能够保证Master-Slave的强一致性。
之前mysql5.6有个Bug: 主库在(2)(3)之间宕机,接着主库故障恢复后,主备之间的复制会中断,备库会报1206的错。
(1)master writes to binlog (writing to kernel buffer) (2)binlog dump threads read the binlog events and send to slaves (3)master flushes to binlog (fsync to binlog file)
为何会有这个Bug产生呢?缘由是在5.6仅仅只是在writing to kernel buffer阶段持有LOCK_log锁。因此在 fsync()完成以前,binlog dump线程就能够读取主库的binlog,发送到备库去。
为了修复这个bug,主库增长了持有LOCK_log锁的时间,直到fsync()结束后释放。这个改进点退化了半同步复制的性能。由于在5.6中,LOCK_log锁是一个很是热的mutex锁。binlog dump线程和用户线程都须要去持有LOCK_log锁。
不过比较好的是,将持久化参数设置成非严格模式(sync_binlog=0;innodb_flush_log_at_trx_commit=0|2),能够缓解LOCK_log锁带来的性能退化。
对于LOCK_log锁的优化,能够看看这个连接:
http://my-replication-life.blogspot.com/2013/09/dump-thread-enhancement.html
http://www.actionsky.com/docs/archives/129
在主库上,binlog的写入和读取都须要同一把锁来保护,也就是LOCK_log,当写入负载较大时,LOCK_log成为热点锁;而对于dump线程而言,每个dump线程在读取binlog事件时,都须要先持有LOCK_log锁;dump线程越多,引发的竞争越激烈。
当dump线程没法及时获取LOCK_log锁时,就会影响发送binlog到备库的速率,进而影响备库IO线程返回ACK的速率。
拆分的思路也很简单,就是每次写入binlog时,维持该binlog文件末尾的偏移量;在该偏移量以前咱们均可以安全读取binlog文件而无需加锁。
首先区分一点,备库的LOCK_log属于relay log,和主库的LOCK_log属于不一样的类对象。 备库上,SQL线程与IO线程在一种状况下会存在LOCK_log竞争,也就是当前SQL线程执行的relylog和IO线程写入的relaylog是同一个文件时,这时候IO线程和SQL线程使用的是同一个IO CACHE来操做文件,所以必须使用LOCK_log来保证读和写的互斥;
为了分拆LOCK_log,须要实现以下两点: a.SQL线程老是在读取事件时,使用自有的IO CACHE,而不是和IO线程公用IO CACHE b.和主库LOCK_log拆分相似,须要在IO线程写入relay log时,维持文件末尾偏移量,SQL线程能够根据该偏移量安全的读取事件