redis主从结构 -- 复制

2021-09-20  本文已影响0人  布拉德老瓜

单机redis不能满足分区容错,当主机发生单点故障的时候,redis服务就无法访问;如果主机的磁盘设备损坏,甚至有丢失数据的风险。可以通过主从结构来避免单点故障的问题,当主机不可用的时候,从机仍然保持着数据的备份。必要时,可以将从机升级为主机来对外提供服务。

主从结构的需要关注两个重要问题分别是:主从数据的一致性和主从机器的存活状态。本文主要关注主从数据的一致性,即主从复制。

redis主从结构的建立是通过在从机服务器执行SLAVEOF命令实现的,该命令让从服务器连接主服务器,并去复制主服务器的数据。下文皆以master和slave分别指代主、从服务器。


slaveof建立主从结构

1.复制功能的实现 (redis2.8以前)

复制功能包含两个阶段: 同步和命令传播

1.1 同步的过程

  1. slave 向 master 发送 SYNC 命令
  2. master收到slave的SYNC命令, 执行BGSAVE操作,在后台生成数据快照RDB文件, 同时使用一个复制积压缓冲区来记录从生成快照开始,执行的来自于客户端的写命令。
  3. BGSAVE完成,master 将生成的RDB文件发给slave。slave接收到该文件之后载入文件,更新自己的数据库,使数据同步到与 master 在 BGSAVE 开始时相同的状态。
  4. master将缓冲区内的所有写命令发送给 slave, slave执行这些命令,更新自己的数据库,让自己更新至当前master所处的状态


    image.png

1.2 命令传播

同步过程完成后,master收到来自客户端的写请求,执行后会出现主从状态不一致的情况。为了使二者一致,master需要将这些命令发送到slave,slave执行完这些命令,两者重新回到一致的状态。

1.3 旧版本复制的缺点

每当slave连接master的时候,都需要完成同步,同步通过BGSAVE生成master中全量数据的快照,然后将快照发送给slave。这在slave初次连接到master的时候是有必要的,但是重连情况下,真的有必要全量复制吗?
slave与master断开连接期间,如果master执行的写入命令很少,为了同步这少量的数据,做全量同步是不划算的一件事。因为BGSAVE需要在后台耗费大量的CPU、内存和磁盘IO资源,同时将全部数据发送给slave需要占用较大的网络带宽和流量,影响master对客户端请求的响应。
那么能不呢在开销较小的情况下将这些增量数据同步至重连的客户端呢?有,新版本的复制支持部分重同步就是做这件事的。

2.新版本复制功能

新版本复制功能是通过PSYNC实现的,它具有完整重同步和部分重同步两种模式。

思考一下为了同步在连接丢失期间内master的写入数据,我们需要那些信息?

  1. 首先需要一个容器,记录master的写入命令。 对这个容器有以下要求:第一,不能无限大,否则写入命令就能把内存给占满了;第二,既然容量有限,那么应当优先存放最近写入的命令。基于这两个要求,可以使用有限队列,由于FIFO的特性,当队列满了之后,将头部节点给remove掉,然后在尾部添加新的数据。redis由复制积压缓冲区来承担该角色。它是一个默认大小1MB的FIFO队列。
  2. 需要master和slave在恢复连接时,二者分别执行到的最后最后一条命令在队列中的位置。redis通过复制偏移量(offset)来完成。
    • master每次向slave发送N byte的数据时,将自己的offset 加 N
    • slave每次收到master发送来的N byte数据时,将自己的offset 加 N
  3. slave向master请求重连时,master如何能保证该slave之前就是从自己这里同步的数据呢?显然需要一个身份凭证,redis通过运行Id来完成校验。即slave内需要保存master的运行Id,重连时,带上该Id,master校验这是自己的id,然后才能向slave发送增量数据,否则将其视为需要全量同步。

2.1 复制积压缓冲区

redis内复制积压缓冲区结构如图:它包含了每个字节的值和其对应的偏移量。


buffer构造

2.2服务器offset

服务器内offset如下图: master与slave最近一次命令传播之后,双方offset都在10086. 之后,slave A与master断开连接。在连接断开期间,master收到了新的命令, 之后又向slave传播了33字节数据。此时各服务器的offset状态如图。


服务器内offset

假设此时slave A请求重连, 那么他需要向master发送重连请求,并带上自己的offset和内部保存的master运行Id。master收到请求后,校验运行id,然后从buffer内查找slave offset + 1是否在buffer内,如果不在,说明这段时间内请求较多,已经将这个数据给挤出了缓冲区,那么此时就不能进行部分同步,因为这样必然要丢失数据。如果在,那么将slave offset + 1 到 master offset的数据发送给slave即可。

2.3 PSYNC命令(partial sync)

PSYNC有两种调用方式:

主服务器收到PSYNC后,有两种正常返回值和异常返回:

同步之后,命令传播就和旧版本如出一辙了,在此不作赘述。

总结

上一篇下一篇

猜你喜欢

热点阅读