Redis | 第11章 服务器的复制《Redis设计与实现》


前言

参考资料:《Redis设计与实现 第二版》;

第四部分为多机数据库的实现,主要由以下模块组成:复制Sentinel集群

本篇将介绍 Redis 的复制功能。在 Redis 中,用户可以通过执行 SLAVEOF 命令或者设置 salveof 选项,让一个从服务器复制主服务器。

与本章相关的 Redis 命令总结在下篇文章,欢迎点击收藏,本篇将不再重复:

《Redis常用命令及示例总结(API)》https://blog.csdn.net/dlhjw1412/article/details/119713214


1. 旧版复制功能的实现

  • 旧版指 Redis 2.8 以前版本;
  • 旧版 Redis 的复制功能分为同步(sync)和命令传播(command propagate)两个操作:
    • 同步操作:将从服务器的数据库状态更新至主服务器当前所处的数据库状态;
    • 命令传播:用于在主服务器的数据库状态被修改,导致主从服务器的数据库状态出现不一致时,让主从服务器重新回到一致状态;

1.1 同步与命令传播

  • 同步:客户端向从服务器发送 SLAVEOF 命令,从服务器执行以下步骤:
    • 1)从服务器向主服务器发送 SYNC 命令;
    • 2)收到 SYNC 命令的主服务器执行 BGSAVE 命令,在后台生成 RDB 文件,并使用一个缓冲区记录此刻开始执行的所有写命令;
    • 3)当主服务器完成 BGSAVE 命令时,将 RDB 文件发给从服务器,从服务器接收并载入 RDB 文件,将数据库状态更新至主服务器执行 BGSAVE 命令时的数据库状态;
    • 4)主服务器将记录在缓冲区里的所有写命令发送给从服务器,从服务器执行写命令,将数据库状态更新至主服务器数据库当前的状态;

同步通信示例

  • 命令传播:同步操作完成后,主服务器会将自己执行的写命令,发送给从服务器。从服务器执行后,主从服务器数据库状态再次回到一致状态;

1.2 旧版复制功能的缺陷

  • 处于命令传播阶段的主从服务器因为网络问题中断了复制,重连后从服务器会向主服务器发送 SYNC 命令执行同步操作;

旧版复制功能的缺陷

  • SYNC 命令非常消耗资源:
    • 主服务器执行 BGSAVE 命令时:耗费主服务器大量 CPU、内存与磁盘 IO 资源;
    • 主服务器将 RDB 文件发给从服务器时:耗费主从服务器大量网络资源(流量和带宽),并对主服务器响应命令请求的时间产生影响;
    • 从服务器在载入 RDB 文件期间:从服务器阻塞无法处理命令请求;

2. 新版复制功能的实现

  • 新版指 Redis 2.8 以后版本;
  • 新版 Redis 的复制功能分为完全重同步(full resynchronization)和部分重同步(partial resynchronization)两个操作:
    • 完全重同步:与初次复制情况相同;
    • 部分重同步:主服务器仅将主从服务器在断线期间的写命令发给从服务器。使用 PSYNC 命令;

新版复制功能的实现

2.1 部分重同步的实现原理

  • 部分重同步依赖以下三个部分:
    • 主从服务器的复制偏移量(replication offset);
    • 主服务器的复制积压缓冲区(replication backlog);
    • 主服务器的运行 ID(run ID);
  • 复制偏移量
    • 执行复制的主从服务器双方会分别维护一个复制偏移量;
    • 主服务器每次向从服务器传播 N 个字节数据时,将自己的复制偏移量加 N;
    • 从服务器每次收到主服务器传播来的 N 字节数据时,将自己的复制偏移量加 N;
    • 通过对比主从服务器的复制偏移量可以判断其数据库状态是否一致;

复制偏移量示例

  • 复制积压缓冲区
    • 复制积压缓冲区由主服务器维护一个固定长度(fixed-size)先进先出(FIFO)队列,默认大小 1MB;
    • 当主服务器进行命令传播时,会将命令发给从服务器,同时将写命令入队到复制积压缓冲区;
    • 重连时,从服务器将自己的复制偏移量 offset 发给主服务器:
      • offset+1 的内容在复制积压缓冲区内,主服务器对从服务器执行部分同步操作;
      • 反之,执行完整重同步操作;
  • 复制积压缓冲区的大小可以根据公式估算:second * write_size_per_second
    • second 为从服务器断线后重连所需平均时间;
    • write_size_per_second 为主服务器平均每秒产生的写命令数据量;
    • 为安全起见,可将复制积压缓冲区大小设置为:上述公式乘 2;

复制积压缓冲区

  • 服务器运行 ID
    • 每个 Redis 都有自己的运行 ID;
    • 运行 ID 在服务器启动时自动生成,由 40 个随机的十六进制字符组成;
    • 从服务器对主服务器进行初次复制时,主服务器会将自己的运行 ID 传送给从服务器,从服务器将这个 ID 保存;
    • 重连时,从服务器向主服务器发送保存的运行 ID:
      • 若从服务器保存的 ID 为当前主服务器运行 ID,根据偏移量判断重同步方式;
      • 反之,执行完整重同步操作;

3. PSYNC 命令的实现

  • PSYNC 命令的调用方法有两种:
    • 从服务器没有复制过任何主服务器,或之前执行过 SLAVEOF NO ONE 命令,那么从服务器在开始复制时向主服务器发送 PSYNC ? -1 命令,主动请求主服务器进行完整重同步;
    • 反之,从服务器发送 PSYNC master_run_id offset 命令,master_run_id 为上一次复制时的主服务器的运行 ID,offset 为从服务器当前的复制偏移量;
  • 主服务器接受 PSYNC 命令后会产生三种回复:
    • 完整重同步:返回 +FULLRESYNC master_run_id offset,master_run_id 为当前主服务器的运行 ID,offset 为主服务器当前的复制偏移量;
    • 部分重同步:返回 +CONTINUE,从服务器只需要等待主服务器发送自己缺少的部分数据;
    • 错误:返回 -ERR,主服务器版本低于 Redis 2.8,从服务器发送 SYNC 命令,与主服务器执行完整同步操作;

PSYNC 命令的执行情况


4. 复制的详细步骤

  • 该步骤为 Redis 2.8 版本以上
  • 客户端向从服务器发送 SLAVEOF ip port 命令;

4.1 设置主服务器的地址和端口

  • 从服务器将 SLAVEOF ip port 命令的 ip 地址和 port 端口保存到服务器状态里:

    struct redisServer{
        //...
        //主服务器的地址
        char *masterhost;
        //主服务器的端口
        int masterport;
    };
    
  • 从服务器向客户端返回 OK,然后开始复制;

4.2 建立套接字连接

  • 从服务器根据 masterhostmasterport 创建连向主服务器的套接字连接;
  • 套接字连接(connect)成功后,从服务器为该套接字关联一个文件事务处理器,专门用来处理复制工作;
  • 主服务器接受(accept)从服务器的套接字连接后,为该套接字创建响应客户端状态,并将从服务器视作客户端(client);
  • 此时,从服务器具备服务器(server)和客户端(client)双重身份;

建立套接字连接

4.3 发送 PING 命令

  • 从服务器成为主服务器的客户端后,向主服务器发送 PING 命令;
  • PING 命令的作用:
    • 检查套接字的读写状态是否正常;
    • 检查主服务器能否正常处理命令请求;
  • 主服务器对 PING 命令的回复有 3 种情况:
    • 返回命令回复:但从服务器不能在有限时间内读取命令内容,表示主从服务器间网络连接状态不佳。此时从服务器会断开并重新创建连向主服务器的套接字;
    • 返回错误:表示主服务器暂时没法处理从服务器的命令请求。此时从服务器会断开并重新创建连向主服务器的套接字;
    • 返回 PONG:表示网络连接正常,从服务器可以继续执行复制工作;

发送 PING 命令的几种情况

4.4 身份验证

  • 从服务器收到 PONG 回复后,根据是否设置 masterauth 选项决定是否进行身份验证;

    • 从服务器没有设置 masterauth 选项,不进行身份验证;
    • 从服务器设置了 masterauth 选项,需要进行身份验证;
  • 在需要进行身份验证的情况下,从服务器给主服务器发送 AUTH password 命令,命令的参数 password 为从服务器 masterauth 选项的值;

  • 从服务器在进行身份验证时根据:主服务器的 requirepass 选项和从服务器的 masterauth 选项不同,可能遇到以下情况:

    主服务器的 requirepass 选项从服务器的 masterauth 选项情况
    设置设置相同则继续复制,不同则返回 invalid password 错误
    设置没有设置返回 NOAUTH 错误
    没有设置设置返回 no password is set 错误
    没有设置没有设置继续复制工作

身份验证的各种情况

4.5 发送端口信息

  • 身份验证完成后,从服务器执行 REPLCONF listening-port port-number 命令,向主服务器发送自己的监听端口号 port-number

  • 主服务器接受后,存储在从服务器的客户端状态 slave_listening_port 属性中:

    typedef struct redisClient{
        //...
        // 从服务器的监听端口号
        int slave_listening_port;
    } redisClient;
    

4.6 同步

  • 从服务器向主服务器发送 PSYNC 命令,执行同步操作,将自己的数据库更新至主服务器数据库当前所处的状态;
  • 在同步操作执行完后,主服务器会成为从服务器的客户端,理由如下:
    • 完整重同步情况:需要将缓冲区里的写命令发送给从服务器;
    • 部分重同步情况:需要将复制积压缓冲区里的写命令发送给从服务器;

同步操作下主从服务器互为对方的客户端

4.7 命令传播

  • 完成同步后,主服务器进入命令传播阶段,一直将写命令发给从服务器;

5. 心跳检测

  • 在命令传播阶段,从服务器默认每秒向主服务器发送命令 REPLCONF ACK replication_offsetreplication_offset 参数是从服务器当前的复制偏移量;
  • 心跳检测的三个作用:
    • 检测主从服务器的网络连接状态:使用 INFO replication 命令可以查看从服务器最后一次向主服务器发送 REPLCONF ACK 命令距离现在过了多久,一般在 0~1 秒为正常;
    • 辅助 min-slaves 配置选项:当从服务器数量 x 少于 min-slaves-to-write 属性值或 x 个服务器的延迟大于等于 min-slaves-max-lag 属性值时,主服务器拒绝写命令;
    • 检测命令丢失:当主服务器发现从服务器的 replication_offset 参数与自己的不一致时,补发写命令数据;
  • 补发命令数据与部分重同步的区别在于:前者没有断线,后再断线了;
  • Redis 2.8 版本以前没有补发命令数据功能;


最后

新人制作,如有错误,欢迎指出,感激不尽!
欢迎关注公众号,会分享一些更日常的东西!
如需转载,请标注出处!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多氯环己烷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值