问题一 主库宕机后,数据可能会丢失
默认复制方式:异步复制
主从复制默认是异步复制,也就是主库在执行完写操作并将日志写入 binlog 后,不会等待从库的确认,直接返回给客户端。(主库写完 binlog 就可以给客户端返回了)
后续由从库的 IO 线程持续循环向主库的 binlog dump 线程发送请求,以获取最新的 binlog 并保持数据同步。
这种默认的复制方式会导致一个问题:如果主库突然宕机,而从库还没有异步拉取到最新的修改。那这次修改操作就会丢失。
如何解决该问题?
思路: 改同步为异步
要解决这个问题,一个简单的思路就是——改异步为同步,也就是主库在写完 binlog 后,需要等待从库的同步完成的 ACK(从库在将日志写入到本地的 delay log 后返回 ACK),确实全部从库都同步完成后,才 commit 这次修改。我们称这种方式为全同步复制。
全同步复制的方案确实可以解决数据丢失的问题,数据安全性达到了最高。但问题是:需要等待所有从库的 ACK 未免开销太大,并且等等到 ACK 后才 commit事务 使得给客户端响应的时长增加,且失败几率也增加了。
这里为了既可以防止数据丢失,又保证性能。MySQL 采用了最传统的折中法——
安全性和性能的折中——半同步复制
MySQL 5.5 版本开始引入了半同步复制机制来降低数据丢失的概率。不像全同步复制一样等待全部从库的 ACK,而是主库在事务提交后等待从库 ACK(所以叫 after-commit
),只要等待到一个从库的 ACK,就向客户端返回操作成功的结果。这种方式称为半同步复制。
更安全的版本——增强版同步复制
MySQL 5.5
引入的半同步复制在事务提交后等待 ACK,虽然没有阻塞用户事务的提交,给客户端比较高的响应速度。但是如果在事务提交但是还没有等到 ACK 的时候主库宕机,就会导致主从数据不一致。因此 MySQL 5.7
在增强半同步复制(无损半同步复制)中引入了AFTER_SYNC
模式,正如模式名而言,是在事务提交之前等待至少一个从库的 ACK。
这种模式以牺牲效率为代价引入了更高的安全性。
问题二 主库从库之间的数据延迟问题
传统的主从复制中,从库中有下面两个线程:
IO Thread
负责去主库拉取binlog
并写入本地的 relay logSQL Thread
负责检测relay log
的变更请求,在从库上重现
问题核心:在传统的 MySQL 主从复制中,从库使用单线程来应用中继日志(relay log)中的事件,这就导致在主库存在大量并发事务时,从库无法并行处理这些事务,从而造成从库复制延迟,主从数据不一致的时间变长。
为了改善复制延迟问题。从 MySQL 5.6
开始,追加了并行复制功能,核心是改单线程的 SQL Thread
为多线程,并行读取重现 relay log
中的日志。在 MySQL 的 5.6、5.7、8.0 版本中,逐步完善了变形复制的机制。下面详细介绍:
并行复制
MySQL 5.6 基于库的并行复制
核心:有多少库就有多少 SQL Thread,每个线程负责一个库的复制。
示例:如果主库上有 db1 和 db2 两个数据库,从库可以使用两个线程分别处理 db1 和 db2 的事务。
局限性:当业务主要集中在一个数据库时(单库多表的场景),基于库的并行复制的效果就不明显。
MySQL 5.7 基于组提交的并行复制
原理:利用主库的组提交信息。在主库上,能够在同一组中提交的事务意味着它们之间没有锁冲突,是可以并行执行的。从库根据主库的组提交信息,将同一组内的事务并行应用到从库上。
优点:从服务器的回放和主服务器是一致的,主服务器怎么并行执行,从服务器就怎么并行回放
相关参数:slave_parallel_type
参数设置为LOGICAL_CLOCK
,slave_parallel_workers
参数设置并行工作线程的数量。
MySQL 8.0 基于 WRITESET 的并行复制
原理:通过分析事务修改的行的主键或者唯一键,判断事务之间是否存在冲突。如果事务修改的行没有重叠,那么这些事务就可以并行执行。这种方式不需要依赖主库的组提交信息,更加灵活。
优点:即使主库没有使用组提交,或者事务没有在主库组提交,只要事务修改的行不冲突,在从库就可以并行执行,进一步提高了并行复制的能力。
相关参数:需要设置binlog_transaction_dependency_tracking
参数为WRITESET
或WRITESET_SESSION
。