一、前置知识

  1. 分布式系统理论基石CAP:consistent-一致性,availability-可用性,partition tolerance-分区容忍性。
  2. 网络分区:网络断开也叫网络分区,当网络分区发生时,一致性会被破坏,除非牺牲可用性,等数据变一致之后再提供服务。所以一致性和可用性只能二选一
  3. 高可用需要满足的三个条件:1)数据备份在不同节点上,防止数据丢失 2)故障自动转移,正在服务的节点发生故障时,可以自动切换到备用节点 3)在线扩容(缩容),可以根据需要动态增加或者减少服务实例

二、什么是高可用?

高可用需要满足以下两个条件:
1、数据尽量不丢失。2、服务尽可能提供。
数据不丢失,有AOF和RDB帮助持久化数据,保证数据尽量不丢失;主从复制就是增加数据副本,即使一个实例宕机,其他实例也能提供服务。AOF和RDB的介绍见数据持久化之AOF和RDB(后续补充完善),本文主要谈谈什么是主从复制。

三、主从复制

1、概念

主从复制简单来说就是将主节点的数据复制到其他所有从节点上的过程。redis的主从复制是单向的,只能从主节点复制到从节点,一个主节点可以有多个从节点,一个从节点只能有一个主节点,因此只有主节点能够提供写服务,其他节点都是提供读服务的。

2、如何保证数据的一致性?

主从节点之间的读写操作是分离的,主节点可以提供读写服务,而从节点只提供读服务,这样可以保证所有副本数据是一致的,但是redis只保证最终一致性。redis的数据复制是异步的,主从节点之间异步确认需要处理的数据量,所以无法保证实时的一致性,这样做的好处是时延低、性能高、保证redis的可用性。

四、同步方式

主从同步的方式根据不同阶段、不同情况也会不同。大致分为三种:
a. 第一次主从同步,全量复制;
b. 主从正常运行期间的同步,增量复制;
c. 主从库间网络断开重连同步,视情况两种方式结合

1、什么是全量同步?

在主库中执行一次bgsave命令,将当前内存中的数据全部快照到磁盘文件中,生成一个rdb文件,然后再把rdb文件的内容传送到从节点。从节点接收后,先清空内存,然后载入rdb文件,从节点更新到跟主节点执行bgsave命令前一致的状态,完成后再进行增量同步。每新增加一个从节点,就必须要先进行一次快照同步,然后再进行增量同步。

2、第一次全量复制是怎样实现的?

第一次全量复制分为三个步骤:
a. 主从建立连接
b. 主库同步数据到从库
c. 发送新增的写命令到从库

a.主从建立连接

主从连接是由从库发起建立的,从库发送replicaof <master_ip> <master_port>命令通知与其连接的主库,并发送psync命令告知同步开始,主库回复确认后,就可以正式开始同步数据了。

b.主库同步数据到从库

主库会将当前redis状态生成为一个RDB文件,发送给从库,并且为每一个从库在内存中申请一块replication buffer,用来记录发送RDB之后新收到的写命令。从库在收到RDB文件之后,会清空内存,并将RDB文件的内容加载的内存中。

c.发送新增的写命令到从库

当从库将RDB文件加载完成后,主库会将replication buffer中的数据发送给从库,从库执行后会将数据跟主库同步到一致

3、什么是replication buffer?

replication buffer是在主节点上创建的一个缓冲区,主节点会为每一个从节点创建一个,用来存放从master生成rdb文件以后一直到slave将rdb文件加载到内存中,这期间所有的写数据。实际上无论是客户端还是slave,只要跟master通信,master都会为其单独申请一个内存buffer,用来进行数据交互,只不过跟slave进行数据同步的这个buffer专门用来存储写命令,所以通常称之为replication buffer。

4、什么是增量同步?

redis2.8以后,网络断开重连后,master和slave之间的数据同步,是通过增量同步实现的,即master通过发送网络断开期间执行的写指令到slave。主节点将修改自身状态的指令缓存到repl_backlog_buffer中,repl_backlog_buffer中会记录所有的写操作,但是由于内存限制,repl_backlog_buffer被设计成环形队列,如果存满了,最后面来的写指令会将最前面的写指令覆盖掉。那从节点就无法通过指令流来同步,只能通过全量同步来实现同步了。主节点用master_repl_offset来标记写操作执行到环形队列的位置,从节点用slave_repl_offset来标记同步到环形队列的位置。网络正常时master_repl_offset和slave_repl_offset一致,当网络断开后,master_repl_offset会持续增加,当网络连接重新恢复,从节点会将runID和slave_repl_offset发送给主节点,主节点只需将这两个offset之间的数据同步到从节点即可。

redis高可用之主从复制_中间件

5、主从正常运行期间的同步,增量复制

主从实现全量同步以后,会一直维持着这个连接,主库会持续不断地将接收到的写命令发送给从库,这个过程也叫做基于长连接的命令传播,可以减少连接建立和断开的开销。

6、主从库间网络断开重连同步,视情况两种方式结合


网络断开重连时,从库会通过psync命令,将replicationID(runID)和slave_repl_offset发送给主库,主节点根据接受到的psync命令和当前服务器状态,决定执行全量复制还是部分复制,replicationID 与从节点发送的 replicationID 相同时,且从节点发送的 slave_repl_offset之后的数据在 repl_backlog_buffer缓冲区中都存在,将进行增量复制,从节点等待主节点发送其缺少的数据即可,当replicationID 与从节点发送的 replicationID 不同,或者从节点发送的 slave_repl_offset 之后的数据已不在主节点的 repl_backlog_buffer缓冲区中,则回复从节点 FULLRESYNC ,表示要进行全量复制,其中 replicationID 表示主节点当前的 replicationID,offset 表示主节点当前的 offset,从节点保存这两个值,以备使用。

五、主从如何知道网络是否断开呢?

主从之间通过心跳来检测。主每隔一段时间向从发送PING命令,从默认每秒一次发送replconf ack ,这样起到的作用是检测主从服务器的网络连接状态,辅助实现 min-slaves选项,检测命令丢失,从节点发送了自身的 slave_replication_offset,主节点会用自己的master_replication_offset对比,如果从节点数据缺失,主节点会从repl_backlog_buffer缓冲区中找到并推送缺失的数据。offset和repl_backlog_buffer缓冲区,不仅可以用于部分复制,也可以用于处理命令丢失等情形;区别在于前者是在断线重连后进行的,而后者是在主从节点没有断线的情况下进行的

六、replication buffer和repl_backlog_buffer的区别和联系

replication buffer是master和每个客户端(slave)进行数据交换的唯一通道,采用TCP长连接的方式,传递写命令。master每写一个命令,都会把命令同时写入这两个缓冲区,replication buffer在收到写命令后会立即将命令发送给客户端,而repl_backlog_buffer则会保存一段时间,直到后面的写命令将其覆盖。在主从同步期间,如果主从连接断开,replication buffer中的命令就会写入失败,当从节点重新发送psync命令进行重连时,会把自己的offset传递给主节点,主节点根据自己的offset和接收到的offset,在环形缓冲区中查找到增量命令,就会把这些增量命令传到replication buffer,然后传递给客户端(slave),这样就实现了一次增量同步。

七、扩展知识

1、无盘复制

主服务器直接通过套接字将快照内容发送到从节点,生成快照是一个遍历的过程,主节点一边遍历内存,一边将序列化的内容发给从节点。从节点将接收到的内容先存在磁盘文件中,再一次性加载。正常情况下,全量重同步需要在磁盘上创建一个RDB文件,然后从磁盘中加载进内存,slave以此进行数据同步。如果磁盘性能很差,则子进程直接发送RDB文件给slave,无需使用磁盘。

2、wait

redis的复制是异步的,但是如果使用wait命令,可以让异步复制变成同步复制,确保系统的强一致性,但这样也就会失去可用性。

wait 1 0 // 第一个参数是从库数量,第二个是等待时间ms
//表示等待wait指令之前所有写操作都同步到从库,然后再执行

欢迎关注 公众号 晴天码字 将输出更多精彩内容