写点什么

详解 Redis 的主从同步原理

作者:C++后台开发
  • 2023-03-07
    湖南
  • 本文字数:2698 字

    阅读完需:约 9 分钟

详解Redis的主从同步原理

前言

Redis 为了保证服务高可用,其中一种实现就是主从模式,即一个 Redis 服务端作为主节点,若干个 Redis 服务端作为主节点的从节点,从而实现即使某个服务端不可用时,也不会影响 Redis 服务的正常使用。本篇文章将对主从模式中为了保证主节点和从节点数据一致而实现的主从同步机制进行学习。

正文

一. 主从数据同步概述

Redis 主从模式中,一个高可用的 Redis 服务由一个 Redis 主节点(Master,后续简称为主节点)和若干 Redis 从节点(Slave,后续简称为从节点)组成。

Redis 中采用读写分离来保证主节点和从节点之间的数据一致性,具体实现如下。

  • 主节点支持数据写入数据读取,从节点只支持数据读取

  • 主节点会与从节点之间执行主从数据同步,以保证主节点数据与从节点数据一致。

主从数据同步分为如下几种情况。

  • 从节点与主节点建立连接时进行全量同步;

  • 主节点与从节点正常运行时的同步;

  • 主节点与从节点连接断开后又重连时会进行增量同步或全量同步。

本篇文章将对 Redis 中的主从数据同步的几种情况进行学习。

二. 从节点与主节点建立连接时的全量同步

从节点与主节点建立连接时的全量同步可以用下图进行示意。

​对于上图所示步骤,说明如下。

  1. 从节点通过配置文件中的 replicaof {masterip} {port} 获得主节点 ip port,然后向主节点发送 psync {repID} {offset} 指令,其中 repID 表示主节点唯一标识,offset 为复制偏移量,因为当前从节点与主节点尚未连接,且尚未开始复制,所以 repID ?offset 为-1;

  2. 主节点收到 psync {repID} {offset} 指令后,会响应从节点并发送 fullresync {repID} {offset} 指令,从节点会将主节点的 repID offset 保存下来;

  3. 主节点收到 psync {repID} {offset} 指令后,会执行 bgsave 异步的生成 RDB 文件,然后主节点将 RDB 文件发送给从节点,从节点接收到 RDB 文件后,会清空内存数据,然后加载 RDB 文件的数据到内存中;

  4. 由于主节点生成 RDB 文件时是异步生成的,此时主节点是非阻塞的,可以继续处理业务,所以在生成 RDB 文件期间发送 RDB 文件期间从节点加载 RDB 文件期间主节点执行的写指令均会存放到缓冲区 replication_buffer 中,所以当从节点加载完 RDB 文件后,主节点会将 replication_buffer 中的内容发送给从节点,从节点会执行 replication_buffer 中的指令,从而达到和主节点一致的状态。

特别说明:在全量同步期间,主节点是非阻塞的,同时从节点很大程度上是非阻塞的,从节点的非阻塞表现在可以通过配置让从节点在全量同步期间使用旧内存数据来处理查询指令,但是从节点在删除旧内存数据并加载 RDB 文件数据到内存中这段时间里,从节点是阻塞的(4.0 版本前,删除旧数据和加载 RDB 文件都会阻塞从节点,4.0 版本开始,删除旧数据可以通过配置变成不阻塞从节点,但是加载 RDB 文件还是会阻塞从节点)。

最后说明一个异常情况,那就是 replication_buffer 是有大小限制的,如果 replication_buffer 大小超过了限制,主节点会断开与从节点的同步连接,此时 replication_buffer 的数据会被清空,然后会重新开始全量同步,所以 replication_buffer 大小需要设置一个合理值。

更多 C++后端开发技术点知识内容包括 C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流媒体,音视频开发,Linux 内核,TCP/IP,协程,DPDK 多个高级知识点。

【文章福利】另外还整理一些 C++后台开发架构师 相关学习资料,面试题,教学视频,以及学习路线图,免费分享有需要的可以点击 C++后端学习资料 免费领取

三. 主节点与从节点正常运行时的同步

参见redis.io/docs/manual…中的一段话。

When a master and a replica instances are well-connected, the master keeps the replica updated by sending a stream of commands to the replica to replicate the effects on the dataset happening in the master side due to: client writes, keys expired or evicted, any other action changing the master dataset.

即正常运行期间,主节点会向从节点发送写指令流来同步主节点的数据变更到从节点。

四. 主节点与从节点断开连接又重连时的增量同步

在第二节中提到了从节点在启动后并需要与主节点进行全量同步时,会向主节点发送 psync {repID} {offset} 指令,这里先对 repID offset 进行解释。

repID

repID Replication ID,是 Redis 节点作为主节点启动时,或者从节点被晋升为主节点时,该主节点都会生成一个新的 repID(思考一下什么情况还会有旧的 repID),后续连接到该主节点的从节点在第一次全量同步的建立连接阶段会保存一份主节点的 repID,所以具有相同 repID 的节点的数据具有相关性。

offset

offset 即偏移量,可以理解为当前节点的数据的逻辑时间。举个例子,某个节点 A offset 为 500,和节点 A 具有相同 repID 的节点 B offset 为 520,那么表明节点 B 的数据比节点 A 的数据更新,节点 A 需要再执行一些写指令才能够让节点 A 的数据状态和节点 B 一致。

有了上述两点认识,现在思考一个问题:主节点和从节点如果因为某些原因,断开了连接,而断开连接这段时间里主节点又处理了一些写指令,那么从节点重新连接后,应该怎么将断开连接那段时间里的写指令同步给重连的从节点?通常的想法就是再执行一次全量同步,在 2.8 之前的版本,确实是这么实现的,但从 2.8 版本开始,引入了增量同步,具体的实现如下。

  • 主节点维护着一份 repl_backlog_buffer 缓冲区域,叫做复制积压缓冲区,主节点在任何时候执行写指令时,都会将写指令记录在 repl_backlog_buffer 中,repl_backlog_buffer 是一个环形数组,所以当数组满时,后续再添加的写指令会覆盖旧的写指令,因此主节点还使用了一个叫做 master_repl_offset 的偏移量,来记录主节点的存到 repl_backlog_buffer 中的最新写指令的位置,master_repl_offset 就是上面提到的 offset,只不过在主节点中叫做 master_repl_offset

  • 从节点也有一个偏移量叫做 slave_repl_offset,用来记录从节点已经从主节点的 repl_backlog_buffer 中同步到的最新写指令的位置;

  • 主节点收到写指令后,master_repl_offset 增加,从节点从主节点的 repl_backlog_buffer 同步了写指令后,slave_repl_offset 增加;

  • 从节点断开重连后,会向主节点发送 psync {repID} {slave_repl_offset} 指令,此时 slave_repl_offset 通常会小于 master_repl_offset,所以主节点仅需要将 slave_repl_offset master_repl_offset 之间的写指令同步给从节点,这就是增量同步

特别注意:如果 repl_backlog_buffer 中记录的从节点断开连接期间的写指令已经被后续的写指令覆盖,那么此时不能执行增量同步,而是需要执行全量同步,所以需要将 repl_backlog_buffer 的大小设置一个合理的值,来尽可能的保证不出现重连后需要全量同步的情况。

总结

以一张图进行总结。

原文链接:https://juejin.cn/post/7207410405786746940

用户头像

C/C++后台开发技术交流qun:720209036 2022-05-06 加入

还未添加个人简介

评论

发布
暂无评论
详解Redis的主从同步原理_redis_C++后台开发_InfoQ写作社区