MySQL 主从复制概念
- MySQL 主从复制是指数据可以从一个MySQL数据库服务器主节点复制到一个或多个从节点。MySQL 默认采用异步复制方式,这样从节点不用一直访问主服务器来更新自己的数据,数据的更新可以在远程连接上进行,从节点可以复制主数据库中的所有数据库或者特定的数据库,或者特定的表。MySQL 的主从复制是依赖于 binlog 的,也就是记录 MySQL 上的所有变化并以二进制形式保存在磁盘上二进制日志文件。主从复制就是将 binlog 中的数据从主库传输到从库上
MySQL 主从复制原理
- 在主从复制过程中,会基于 3 个线程来操作,一个主库线程,两个从库线程。二进制日志转储线程(Binlog dump thread)是一个主库线程。当从库线程连接的时候,主库可以将二进制日志发送给从库,当主库读取事件的时候,会在 Binlog 上加锁,读取完成之后,再将锁释放掉
- 从库 I/O 线程会连接到主库,向主库发送请求更新 Binlog。这时从库的 I/O 线程就可以读取到主库的二进制日志转储线程发送的 Binlog 更新部分,并且拷贝到本地形成中继日志(Relay log)
- 从库 SQL 线程会读取从库中的中继日志,并且执行日志中的事件,从而将从库中的数据与主库保持同步。
所以能看到主从同步的内容就是二进制日志(Binlog),它虽然叫二进制日志,实际上存储的是一个又一个事件(Event),这些事件分别对应着数据库的更新操作,比如 INSERT、UPDATE、DELETE 等。另外我们还需要注意的是,不是所有版本的 MySQL 都默认开启服务器的二进制日志,在进行主从同步的时候,我们需要先检查服务器是否已经开启了二进制日志。
进行主从同步的内容是二进制日志,它是一个文件,在进行网络传输的过程中就一定会存在延迟(比如 500ms),这样就可能造成用户在从库上读取的数据不是最新的数据,也就是主从同步中的数据不一致性问题。比如我们对一条记录进行更新,这个操作是在主库上完成的,而在很短的时间内(比如 100ms)又对同一个记录进行了读取,这时候从库还没有完成数据的更新,那么我们通过从库读到的数据就是一条旧的记录。
简短回答
Master开启bin-log功能,服务器配置二进制日志(binlog日志),只保留update等的数据。
需要开启三个线程,Master:I/O线程;Slave:I/O线程,SQL线程。
从数据库会请求主数据库的binlog日志,将bin-log日志内容写入到relay-log中继日志,创建一个master.info文件,然后按日志执行
Slave已经开启了sql线程,由sql线程实时监测relay-log日志内容是否有更新,如果有更新,则解析文件中的sql语句,并在Slave数据库中执行相同的操作语句。
如何解决主从同步的数据一致性问题
1. 异步复制
- 异步模式就是客户端提交 COMMIT 之后不需要等从库返回任何结果,而是直接将结果返回给客户端,这样做的好处是不会影响主库写的效率,但可能会存在主库宕机,而 Binlog 还没有同步到从库的情况,也就是此时的主库和从库数据不一致。这时候从从库中选择一个作为新主,那么新主则可能缺少原来主服务器中已提交的事务。所以,这种复制模式下的数据一致性是最弱的。
2. 半同步复制
- MySQL5.5 版本之后开始支持半同步复制的方式。原理是在客户端提交 COMMIT 之后不直接将结果返回给客户端,而是等待至少有一个从库接收到了 Binlog,并且写入到中继日志中,再返回给客户端。这样做的好处就是提高了数据的一致性,当然相比于异步复制来说,至少多增加了一个网络连接的延迟,降低了主库写的效率。
- 在 MySQL5.7 版本中还增加了一个 rpl_semi_sync_master_wait_for_slave_count 参数,我们可以对应答的从库数量进行设置,默认为 1,也就是说只要有 1 个从库进行了响应,就可以返回给客户端。如果将这个参数调大,可以提升数据一致性的强度,但也会增加主库等待从库响应的时间。
3. 组复制
- 组复制技术,简称 MGR(MySQL Group Replication)。是 MySQL 在 5.7.17 版本中推出的一种新的数据复制技术,这种复制技术是基于 Paxos 协议的状态机复制。
- 刚才介绍的异步复制和半同步复制都无法最终保证数据的一致性问题,半同步复制是通过判断从库响应的个数来决定是否返回给客户端,虽然数据一致性相比于异步复制有提升,但仍然无法满足对数据一致性要求高的场景,比如金融领域。MGR 很好地弥补了这两种复制模式的不足。
MGR 是如何工作的(如下图所示)。
- 首先我们将多个节点共同组成一个复制组,在执行读写(RW)事务的时候,需要通过一致性协议层(Consensus 层)的同意,也就是读写事务想要进行提交,必须要经过组里 “大多数人”(对应 Node 节点)的同意,大多数指的是同意的节点数量需要大于(N/2 1),这样才可以进行提交,而不是原发起方一个说了算。而针对只读(RO)事务则不需要经过组内同意,直接 COMMIT 即可。
- 在一个复制组内有多个节点组成,它们各自维护了自己的数据副本,并且在一致性协议层实现了原子消息和全局有序消息,从而保证组内数据的一致性。(具体原理点击这里可以参考。)
MGR 将 MySQL 带入了数据强一致性的时代,是一个划时代的创新,其中一个重要的原因就是 MGR 是基于 Paxos 协议的。Paxos 算法是由 2013 年的图灵奖获得者 Leslie Lamport 于 1990 年提出的,另外作者在 2001 年发布了一篇简化版的文章
事实上,Paxos 算法提出来之后就作为分布式一致性算法被广泛应用,比如 Apache 的 ZooKeeper 也是基于 Paxos 实现的。
- MySQL 主从复制主要用途
- 读写分离:在开发工作中,有时候会遇见某个sql 语句需要锁表,导致暂时不能使用读的服务,这样就会影响现有业务,使用主从复制,让主库负责写,从库负责读,这样,即使主库出现了锁表的情景,通过读从库也可以保证业务的正常运作。
- 数据实时备份,当系统中某个节点发生故障时,可以方便的故障切换
- 架构扩展:随着系统中业务访问量的增大,如果是单机部署数据库,就会导致I/O访问频率过高。有了主从复制,增加多个数据存储节点,将负载分布在多个从节点上,降低单机磁盘I/O访问的频率,提高单个机器的I/O性能。
- 高可用HA
- 读取的负载均衡,通过建立多个从服务器。减少主服务器的压力,一般采用dns的Round-Robin和Linux的LVS功能都可以实现负载平衡
复制状态机:所有分布式存储都是这么复制数据的
在 MySQL 中,无论是复制还是备份恢复,依赖的都是全量备份和 Binlog,全量备份相当于备份那一时刻的一个数据快照,Binlog 则记录了每次数据更新的变化,也就是操作日志。主从同步,也就是数据复制,虽然讲的都是 MySQL,这种基于“快照 操作日志”的方法,不是 MySQL 特有的。
比如说,Redis Cluster 中,它的全量备份称为 Snapshot,操作日志叫 backlog,它的主从复制方式几乎和 MySQL 是一模一样的。
我再给你举个例子,之前我们讲过的 Elasticsearch,它是一个内存数据库,读写都在内存中,那它是怎么保证数据可靠性的呢?对,它用的是 translog,它备份和恢复数据的原理和实现方式也是完全一样的。这些什么什么 log,都是不同的马甲儿而已,几乎所有的存储系统和数据库,都是用这一套方法来解决备份恢复和数据复制问题的。
既然这些存储系统他们实现数据复制的方法是完全一样的,那这几节课我们讲的 MySQL 主从复制时,讲到的那些问题、丢数据的风险,对于像 Redis Cluster、ES 或者其他分布式存储也都是一样存在的。那我们讲的,如何应对的方法、注意事项、最佳实践,这些也都是可以照搬的。
这一套方法其实是有理论基础的,叫做复制状态机 (Replication State Machine),我能查到的最早的出处是 1978 年 Lamport 的一篇论文《The Implementation of Reliable Distributed Multiprocess Systems》。
总结
存储系统中的数据称为状态(也就是 MySQL 中的数据),状态的全量备份称为快照(Snapshot),就像给数据拍个照片一样。我们按照顺序记录更新存储系统的每条操作命令,就是操作日志(Commit Log,也就是 MySQL 中的 Binlog)。你可以对照下面这张图来理解上面这些抽象的概念。
复制数据的时候,只要基于一个快照,按照顺序执行快照之后的所有操作日志,就可以得到一个完全一样的状态。在从节点持续地从主节点上复制操作日志并执行,就可以让从节点上的状态数据和主节点保持同步。
主从同步做数据复制时,一般可以采用几种复制策略。性能最好的方法是异步复制,主节点上先记录操作日志,再更新状态数据,然后异步把操作日志复制到所有从节点上,并在从节点执行操作日志,得到和主节点相同的状态数据。
异步复制的劣势是,可能存在主从延迟,如果主节点宕机,可能会丢数据。另外一种常用的策略是半同步复制,主节点等待操作日志最少成功复制到 N 个从节点上之后,再更新状态,这种方式在性能、高可用和数据可靠性几个方面都比较平衡,很多分布式存储系统默认采用的都是这种方式。
docker,git 就将状态机运用到了极致
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!