详解 MySQL 复制机制
详解 MySQL 复制机制
1. 异步复制
异步复制是 MySQL 自带的最原始的复制方式,主库和备库成功建立复制关系后,在备库上会有一个 I/O 线程去主库拉取 binlog,并将 binlog 写入到本地的中继日志(relay log)中,然后备库会开启另外一个 SQL 线程去回放 relay log,通过这种方式达到 Master-Slave 数据同步的目的。
通常情况下,slave 是只读的,可以承担一部分读流量,而且可以根据实际需要,添加一个或者多个 slave,这样在一定程度上可以缓解主库的读压力;另一方面,若 master 出现异常(crash、硬件故障等),无法对外提供服务,此时 slave 可以承担起 master 的重任,避免了单点的产生,所以复制就是为容灾和提高性能而生的。
2. 半同步复制
一般情况下,异步复制就已经足够应付了,但由于是异步复制,备库极有可能是落后于主库,特别是极端情况下,我们无法保证主备数据是严格一致的(即使我们观察到 Seconds Behind Master 这个值为 0)。比如,当用户发起 commit 命令时,master 并不关心 salve 的执行状态,执行成功后,立即返回给用户。试想下,若一个事务提交后,master 成功返回给用户后 crash,这个事务的 binlog 还没来得及传递到 slave,那么 slave 相对于 master 而言就少了一个事务,此时主备就不一致了。对于要求强一致的业务是不可以接收的,半同步复制就是为了解决数据一致性而产生的。
为什么叫半同步复制?我们先来说说同步复制,所谓同步复制就是一个事务在 master 和 slave 都执行后,才返回给用户执行成功。这里的核心是说 master 和 slave 要么都执行,要么都不执行,设计到 2pc(2 phrase commit)。而 MySQL 只实现了本地 redo-log 和 binlog 的 2pc,但并没有实现 master 和 slave 的 2pc,所以不是严格意义上的同步复制。而 MySQL 半同步复制不要求 slave 执行,而仅仅是接收到日志后,就通知 master 可以返回了。这里的关键点是 slave 接收日志后是否执行,若执行后才通知 master 则是同步复制,若仅仅是接收日志成功,则是半同步复制。
半同步复制如何实现呢?半同步复制实现的关键点是 master 对于事务提交过程特殊处理。目前实现半同步复制主要有两种模式:AFTER_SYNC 模式和 AFTER_COMMIT 模式。两种方式的主要区别在于是否在存储引擎提交后等待 slave 的 ACK。
-
AFTER_COMMIT 模式
start 和 end 分别表示用户发起 commit 命令和 master 返回给用户的时间点,中间部分就是整个 commit 过程 master 和 slave 做的事情。
master 提交时,会首先将该事务的 redo log 刷入磁盘(这里其实还涉及到两阶段提交的问题),然后进入 innodb commit 过程,这个步骤主要是释放锁,标记事务为提交状态(其他用户可以看到该事务的更新),这个过程完成后,等待 slave 发送 ack 消息,等到 slave 的响应后,master 才成功返回给用户,master 和 salve 的同步逻辑,是 master-slave 一致性的保证。
-
AFTER_SYNC 模式
与 AFTER_COMMIT 相比,master 在 AFTER_SYNC 模式下,fsync binlog 后,就开始等待 slave 同步,那么在进行第 5 步 innodb commit 后,即其他事务能看到该事务的更新时,slave 已经成功接收到 binlog,即使发生切换,slave 拥有与 master 同样的数据,不会发生“幻读”现象。但是对于上面描述的第一种情况,结果是一样的。
所以,在极端情况下,半同步复制的 master-slave 会有一个事务不一致,但是对于用户而言,由于这个事务并没有成功返回给用户,所以无论事务提交与否都是可以接受的,用户有必要进行查询或重试,判断是否更新成功。或者我们想想,对于单机而言,若事务执行成功后,返回给用户时,网络断了,用户也是面临一样的问题,所以,这不是半同步的问题,对于提交返回成功的事务,半同步复制保证 master-slave 一定是一致的,从这个角度来看,半同步复制不会丢数据,可以保证 master-slave 的强一致性。
3. 并行复制
半同步复制解决了 master-slave 强一致问题,那么性能问题呢?参与复制的两个线程:I/O 线程和 SQL 线程,分别用于拉取和回放 binlog。对于 salve 而言,所有拉取和解析 binlog 的动作都是串行的,相对于 master 并发处理用户请求,在高负载下,若 master 产生 binlog 的速度超过 slave 消费 binlog 的速度,导致 slave 出现延迟,可以看到,users 和 master 之间的管道远远大于 master 和 salve 之间的管道。
那么如何并行化,并行 I/O 线程,还是并行 SQL 线程?其实两方面都是可以并行的,但是并行 SQL 线程的收益更大,因为 SQL 线程做的事情更多(解析,执行)。并行 I/O 线程,可以将从 master 拉取和写入 relay log 分为两个线程;并行 SQL 线程则可以根据需要做到库级并行,表级并行,事务级并行。库级并行在 MySQL 官方版本 5.6 已经实现了。并行复制框架实际包含了一个协调线程和若干个工作线程。协调线程负责分发和解决冲突,工作线程只负责执行。DB1,DB2 和 DB3 的事务就可以并发执行,提高了复制的性能。有时候库级并发可能不够,需要做表级并发,或更细粒度的事务级并发。
并发复制如何处理冲突?并发世界是美好的,但不能乱并发,否则数据就乱了。master 上面通过锁机制来保证并发的事务有序进行,那么并行复制呢?slave 必须保证回放的顺序与 master 上事务执行顺序一致,因此只要做到顺序读取 binlog,将不冲突的事务并发执行即可。对于库级并发而言,协调线程要保证执行同一个库的事务放在一个工作线程串行执行;对于表级并发而言,协调线程要保证同一个表的事务串行执行;对于事务级而言,则是保证操作同一行的事务串行执行。
是否粒度越细,性能越好?这个并不是一定的。相对于串行复制而言,并行复制多了一个协调线程。协调线程一个重要作用是解决冲突,粒度越细的并发,可能会有更多的冲突,最终可能也是串行执行的,但是消耗了大量的冲突检测代价。
————————————————
版权声明:本文为51CTO博主「隔壁老湿」的原创文章。
原文链接:https://blog.51cto.com/14354846/2400323