一、主从复制的作用
kv存储是一款内存数据库,一旦网络宕机数据直接中断,存在数据丢失的风险。主从复制功能就是用于解决发生单机故障时数据易丢失的问题。配合Sentinel 或 Cluster使用时,单机发生故障可以实现自动切换,从节点可被自动提升为主节点继续提供服务。主从复制的优点如下:
(1)主节点(Master)数据实时复制到从节点(Replica),形成多副本,避免单点故障导致数据丢失。
(2)主节点处理写请求,从节点分担读请求,显著提升系统的读吞吐能力。
(3)配合 Sentinel 或 Cluster,当主节点故障时,从节点可被提升为新的主节点继续提供服务。
主从复制默认是异步的,这意味着主节点在执行完写命令后立即返回客户端结果,无需等待从节点确认,这保证了主节点的高性能。虽然存在极短的主从数据不一致窗口,但通过WAIT命令可以请求同步复制来降低数据丢失风险(只是降低,无法成为一个强一致性的系统)。
二、两种核心同步机制:全量与增量
当从节点与主节点建立连接或同步中断后重连时,同步方式由复制偏移量(Replication Offset)和复制 ID(Replication ID)共同决定。
1、全量同步
触发条件:从节点首次连接,或请求的偏移量已不在主节点的复制积压缓冲区(Replication Backlog)内,又或主从的复制 ID 不匹配。
执行流程(基于状态机设计):
生成 RDB 快照:主节点 fork 子进程执行
bgsave,生成当前数据的 RDB 快照文件。期间所有新写命令被暂存到输出缓冲区。传输 RDB 文件:RDB 生成后发送给从节点。主节点会复用同一次 RDB 生成来服务多个同时请求的从节点,避免资源浪费。
加载与对齐:从节点清空旧数据,加载 RDB。主节点持续缓存新写命令,待从节点加载完成后,通过
REPLCONF ACK通知主节点,主节点再发送缓存命令,完成数据对齐。
2. 增量同步
触发条件:从节点因网络抖动短暂断线重连,且主节点仍保留其断线期间的命令(复制偏移量和 复制 ID均在主节点中能对上)。
实现原理:
复制积压缓冲区:主节点维护一个固定长度的环形缓冲区,存储最近传播的写命令及其偏移量。
断点续传:从节点重连后发送
PSYNC,携带主节点运行ID(Run ID)和已处理的复制偏移量。主节点判断:若 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初始化:从节点存储主节点地址,状态
REPL_STATE_NONE变为REPL_STATE_CONNECT。连接建立:定时任务触发连接,成功后状态变为
REPL_STATE_CONNECTING。主从握手:从节点发送
PING、AUTH认证,并告知自身端口及能力,状态推进至REPL_STATE_SEND_HANDSHAKE等。同步请求:发送
PSYNC,状态转为REPL_STATE_TRANSFER,准备接收 RDB。在线同步: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_END | RDB文件正在后台生成中。 | 主节点执行bgsave,在此期间继续累积写命令。 |
REDIS_REPL_SEND_BULK | RDB文件已生成,正在通过网络发送给从节点。 | 主节点发送RDB文件。 |
REDIS_REPL_ONLINE | RDB文件传输完成,进入命令传播阶段,持续发送增量写命令。 | 主节点持续将新写命令发送给从节点。 |
四、主从命令传播
全量同步完成后,主从节点的数据库处于一致状态。然而,主节点每时每刻都可能收到新的写命令,这会让主从数据再次变得不一致。为了维持一致,命令传播机制会像一个单向的管道,将主节点上执行的、会改变数据的命令,持续不断地发送给所有已连接的从节点。从节点接收到这些命令后,只需照单全收并在本地重放执行,就能让自身数据库状态再次跟上主节点的步伐。命令传播能高效运行,主要依靠两个关键设计:
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等技术内容,点击立即学习:链接