前言

参考资料:《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 命令,与主服务器执行完整同步操作;

4. 复制的详细步骤

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

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

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

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

4.2 建立套接字连接

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

4.3 发送 PING 命令

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

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 属性中:

    1. typedef struct redisClient{
    2. //...
    3. // 从服务器的监听端口号
    4. int slave_listening_port;
    5. } 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 版本以前没有补发命令数据功能;

最后

新人制作,如有错误,欢迎指出,感激不尽!
欢迎关注公众号,会分享一些更日常的东西!
如需转载,请标注出处!

Redis | 第11章 服务器的复制《Redis设计与实现》的更多相关文章

  1. 第11章 Media Queries 与Responsive 设计

    Media Queries--媒体类型(一) 随着科学技术不断的向前发展,网页的浏览终端越来越多样化,用户可以通过:宽屏电视.台式电脑.笔记本电脑.平板电脑和智能手机来访问你的网站.尽管你无法保证一个 ...

  2. redis 笔记04 服务器、复制

    服务器 1. 一个命令请求从发送到完成主要包括以下步骤: 1). 客户端将命令请求发送给服务器 2). 服务器读取命令请求,并分析出命令参数 3). 命令执行器根据参数查找命令的实现函数,然后执行实现 ...

  3. Redis | 第7章 Redis 服务器《Redis设计与实现》

    目录 前言 1. 命令请求的执行过程 1.1 发送命令请求 1.2 读取命令请求 1.3 命令执行器(1):查找命令实现 1.4 命令执行器(2):执行预备操作 1.5 命令执行器(3):调用命令的实 ...

  4. Redis——学习之路二(初识redis服务器命令)

    上一章我们已经知道了如果启动redis服务器,现在我们来学习一下,以及如何用客户端连接服务器.接下来我们来学习一下查看操作服务器的命令. 服务器命令: 1.info——当前redis服务器信息   s ...

  5. Linux下Redis主从复制以及SSDB主主复制环境部署记录

    前面的文章已经介绍了redis作为缓存数据库的说明,本文主要说下redis主从复制及集群管理配置的操作记录: Redis主从复制(目前redis仅支持主从复制模式,可以支持在线备份.读写分离等功能.) ...

  6. Redis02 Redis客户端之Java、连接远程Redis服务器失败

    1 查看支持Java的redis客户端 本博文采用 Jedis 作为redis客户端,采用 commons-pool2 作为连接redis服务器的连接池 2 下载相关依赖与实战 2.1 到 Repos ...

  7. redis主从复制以及SSDB主主复制环境部署记录(四)

    redis配置主从和ssdb主主复制 参考:散尽浮华Linux下Redis主从复制以及SSDB主主复制环境部署记录 只做自己查阅. 环境要求:三台虚拟机一台做主,两台做从. 通过SSDB在redis做 ...

  8. Redis集群搭建,伪分布式集群,即一台服务器6个redis节点

    Redis集群搭建,伪分布式集群,即一台服务器6个redis节点 一.Redis Cluster(Redis集群)简介 集群搭建需要的环境 二.搭建集群 2.1Redis的安装 2.2搭建6台redi ...

  9. Redis | 第9章 Lua 脚本与排序《Redis设计与实现》

    目录 前言 1. Lua 脚本 1.1 Redis 创建并修改 Lua 环境的步骤 1.2 Lua 环境协作组件 1.3 EVAL 命令的实现 1.4 EVALSHA 命令的实现 1.5 脚本管理命令 ...

随机推荐

  1. mysql登录后重置root密码的步骤

    mysql重置root密码. 方法一: 编辑配置文件 /etc/my.cnf ,在[mysqld]后面任意一行添加"skip-grant-tables"用来跳过密码验证 接下来我们 ...

  2. 修改linux 两种时间的方法

    1,整理了一下怎么修改linux 两种时间的方法. 硬件时间:hwclock 或者clock,设置的方法是 hwclock --set --date="05/12/2018 12:30:50 ...

  3. Hive计算最大连续登陆天数

    目录 一.背景 二.算法 1. 第一步:排序 2. 第二步:第二列与第三列做日期差值 3. 第三步:按第二列分组求和 4. 第四步:求最大次数 三.扩展(股票最大涨停天数) 强哥说他发现了财富密码,最 ...

  4. svg的animate动画动态加载删除遇到删除animate后再次加载的animate动画没有效果问题

    svg上有多个圆圈,当选中特定圆圈后给其加上animate动画效果,并把其他圆圈的animate效果去除. 第一次选择一个点实现动画效果完全达到效果,因为是第一次所以不需要把其他圆圈的animate子 ...

  5. 攻防世界 Misc 新手练习区 ext3 bugku Writeup

    攻防世界 Misc 新手练习区 ext3 bugku Writeup 题目介绍 题目考点 WinHex工具的使用 linux磁盘挂载mount命令 Writeup 下载附件拖进winhex分析一下,查 ...

  6. docker容器命令(一)

    容器命令 创建容器:docker run 参数: -it 交互 -d 后台 –name 容器名 -p 主机端口:容器端口 (主机端口映射到docker端口) docker run --name cen ...

  7. NCF 中如何将Function升级到FunctionRender

    简介 历史的车轮在不断的向前推进,NCF也在不断的迭代更新,只为成为更好的NCF 如果你之前没有用过NCF可以跳过这个文档,直接去下载最新的NCF源码进行实践. NCF仓库地址:https://git ...

  8. Linux usb 1. 总线简介

    文章目录 1. USB 发展历史 1.1 USB 1.0/2.0 1.2 USB 3.0 1.3 速度识别 1.4 OTG 1.5 phy 总线 1.6 传输编码方式 2. 总线拓扑 2.1 Devi ...

  9. macos command 'clang' failed with exit status 1

    export CC=$(which gcc)export CXX=$(which g++)pip install fbprophet CC=clang pip install gevent

  10. spring定时任务ThreadPoolTaskScheduler使用注意事项之线程池大小

    背景 最近小伙伴解决了一个工单,描述为"手工推送案件无法推,提示token失效",当前工单状态为待关闭,解决方案为"东软接口不稳定造成的,东软的接口恢复正常后,问题解决& ...