kv存储主从复制的设计与实现

📅 2026/7/25 15:44:31 👁️ 阅读次数 📝 编程学习
kv存储主从复制的设计与实现
一、主从复制的作用

kv存储是一款内存数据库,一旦网络宕机数据直接中断,存在数据丢失的风险。主从复制功能就是用于解决发生单机故障时数据易丢失的问题。配合Sentinel 或 Cluster使用时,单机发生故障可以实现自动切换,从节点可被自动提升为主节点继续提供服务。主从复制的优点如下:

(1)主节点(Master)数据实时复制到从节点(Replica),形成多副本,避免单点故障导致数据丢失。

(2)主节点处理写请求,从节点分担读请求,显著提升系统的读吞吐能力。

(3)配合 Sentinel 或 Cluster,当主节点故障时,从节点可被提升为新的主节点继续提供服务。

主从复制默认是异步的,这意味着主节点在执行完写命令后立即返回客户端结果,无需等待从节点确认,这保证了主节点的高性能。虽然存在极短的主从数据不一致窗口,但通过WAIT命令可以请求同步复制来降低数据丢失风险(只是降低,无法成为一个强一致性的系统)。

二、两种核心同步机制:全量与增量

当从节点与主节点建立连接或同步中断后重连时,同步方式由复制偏移量(Replication Offset)复制 ID(Replication ID)共同决定。

1、全量同步

触发条件:从节点首次连接,或请求的偏移量已不在主节点的复制积压缓冲区(Replication Backlog)内,又或主从的复制 ID 不匹配。

执行流程(基于状态机设计):

  1. 生成 RDB 快照:主节点 fork 子进程执行bgsave,生成当前数据的 RDB 快照文件。期间所有新写命令被暂存到输出缓冲区。

  2. 传输 RDB 文件:RDB 生成后发送给从节点。主节点会复用同一次 RDB 生成来服务多个同时请求的从节点,避免资源浪费。

  3. 加载与对齐:从节点清空旧数据,加载 RDB。主节点持续缓存新写命令,待从节点加载完成后,通过REPLCONF ACK通知主节点,主节点再发送缓存命令,完成数据对齐。

2. 增量同步

触发条件:从节点因网络抖动短暂断线重连,且主节点仍保留其断线期间的命令(复制偏移量和 复制 ID均在主节点中能对上)。

实现原理

  1. 复制积压缓冲区:主节点维护一个固定长度的环形缓冲区,存储最近传播的写命令及其偏移量。

  2. 断点续传:从节点重连后发送PSYNC,携带主节点运行ID(Run ID)和已处理的复制偏移量。

  3. 主节点判断:若 Run ID 匹配且偏移量仍在积压缓冲区内,主节点返回+CONTINUE,从该偏移量起发送增量命令,避免全量 RDB 传输。

三、关键状态机:复制与同步

kv存储引擎通过状态机严格管理复制流程。从节点的核心状态变迁如下:

主节点:192.168.137.13:9096 从节点:192.168.137.14:9096 配置主从关系:redis-cli -h 192.168.137.14 -p 9096 REPLICAOF 192.168.137.13 9096
  1. 初始化:从节点存储主节点地址,状态REPL_STATE_NONE变为REPL_STATE_CONNECT

  2. 连接建立:定时任务触发连接,成功后状态变为REPL_STATE_CONNECTING

  3. 主从握手:从节点发送PINGAUTH认证,并告知自身端口及能力,状态推进至REPL_STATE_SEND_HANDSHAKE等。

  4. 同步请求:发送PSYNC,状态转为REPL_STATE_TRANSFER,准备接收 RDB。

  5. 在线同步:RDB 加载完成,状态进入REPL_STATE_CONNECTED,转为长连接命令流同步。

从节点状态
状态常量含义所处阶段
REDIS_REPL_NONE初始状态,没有进行任何复制。初始化
REDIS_REPL_CONNECT需要连接主节点。执行replicaof命令后进入此状态,等待周期任务触发连接。初始化
REDIS_REPL_CONNECTING正在与主节点建立TCP连接。建立连接
REDIS_REPL_RECEIVE_PONG连接建立后,等待主节点回复PONG,这是握手的第一步。主从握手
REDIS_REPL_TRANSFER正在接收主节点发来的RDB文件(全量同步数据)。复制类型判断与执行
REDIS_REPL_CONNECTED同步完成,已与主节点保持连接,正在接收实时的增量命令。复制类型判断与执行
主节点状态
状态常量含义主节点动作
REDIS_REPL_WAIT_BGSAVE_START准备为从节点生成RDB文件,但正在等待后台保存任务开始。可能等待其他bgsave任务完成。
REDIS_REPL_WAIT_BGSAVE_ENDRDB文件正在后台生成中。主节点执行bgsave,在此期间继续累积写命令。
REDIS_REPL_SEND_BULKRDB文件已生成,正在通过网络发送给从节点。主节点发送RDB文件。
REDIS_REPL_ONLINERDB文件传输完成,进入命令传播阶段,持续发送增量写命令。主节点持续将新写命令发送给从节点。
四、主从命令传播

全量同步完成后,主从节点的数据库处于一致状态。然而,主节点每时每刻都可能收到新的写命令,这会让主从数据再次变得不一致。为了维持一致,命令传播机制会像一个单向的管道,将主节点上执行的、会改变数据的命令,持续不断地发送给所有已连接的从节点。从节点接收到这些命令后,只需照单全收并在本地重放执行,就能让自身数据库状态再次跟上主节点的步伐。命令传播能高效运行,主要依靠两个关键设计:

1、复制积压缓冲区(Replication Backlog):增量同步的基石

为了应对从节点可能发生的短暂断线,Redis 在命令传播的同时,会将所有写命令也写入一个由主节点维护的 复制积压缓冲区(Replication Backlog) 中。

  • 工作原理:这个缓冲区是一个固定长度的队列(deque),其大小可在配置文件中调整(默认1MB)。当从节点断线重连后,它会向主节点报告自己已经执行的复制偏移量(Offset)。

  • 决策过程:主节点会检查从节点报告的偏移量之后的数据,是否仍然存在自己的积压缓冲区中。如果在,主节点就会进行增量同步,只发送断线期间缺失的那部分命令,效率极高。如果不在,则意味着断线时间过长,增量同步已不可能,只能退回全量同步。

2、心跳检测与命令丢失发现:保证主从连接的可靠性

在长期运行的系统中,网络抖动可能导致命令在传输途中丢失,但连接并未断开。为此,Redis 引入了心跳检测机制。在命令传播阶段,从节点会以每秒一次的频率,向主节点发送一个包含自身当前复制偏移量的REPLCONF ACK <offset>命令。这个心跳主要有两个作用:

检测连接状态:主节点如果长时间收不到从节点的心跳,就能感知到连接异常。

发现命令丢失:主节点收到心跳后,会对比心跳中的偏移量和自己本地的偏移量。如果发现从节点的偏移量比自己的小,就说明从节点丢失了一些已传播的命令。此时,主节点会主动从积压缓冲区中找到这些缺失的命令,并重新发送给从节点,从而保证了数据的最终一致性。

KV存储引擎主从复制是在内存容量、网络带宽与数据一致性之间做出的精妙权衡。它利用RDB 快照应对大规模全量同步,又借助复制积压缓冲区化解偶发断线重连。复制 ID 与偏移量的组合精准定义了数据版本。主从同步是高可用设计的基本环节,更能为构建稳定可靠的分布式系统提供宝贵的思路。

推荐一个零声教育学习教程,个人觉得老师讲得不错,分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,点击立即学习:链接