一、前言:主从同步不是"简单拷贝"
很多开发者认为 Redis 主从同步就是"主节点把数据发给从节点",
但真正的挑战在于:
- 如何在不停写的情况下完成全量同步?
- 网络闪断后,如何避免重新全量同步?
- 从节点如何精确追上主节点的最新状态?
Redis 通过 PSYNC 协议(Partial Synchronization) 实现了高效、可靠的复制机制。
本文将带你深入源码级实现,揭开主从同步背后的精妙设计。
二、主从同步的核心目标与演进
目标
让从节点(Replica)的数据状态最终与主节点(Master)完全一致,且对主节点性能影响最小。
协议演进
| 版本 | 协议 | 缺陷 |
|---|---|---|
| Redis 2.8 之前 | SYNC | 每次断连都全量同步,效率极低 |
| Redis 2.8 ~ 3.x | PSYNC1 | 支持部分重同步,但主节点重启后仍需全量 |
| Redis 4.0+ | PSYNC2 | 支持主节点重启后的部分重同步(终极方案) |
✅ 本文聚焦 PSYNC2(现代 Redis 默认协议)
三、主从同步全流程(含状态机)
整个过程分为两个阶段:
- 全量复制(Full Resynchronization)
- 增量复制(Partial Resynchronization)
四、阶段一:全量复制(首次同步 or PSYNC 失败)
触发场景
- 从节点首次连接主节点
- 主从断连太久,复制积压缓冲区已覆盖所需数据
- 主节点重启且未开启
replid持久化(旧版)
关键设计点
4.1 复制客户端(Replication Client)
- 主节点为每个从节点创建一个虚拟客户端
- 从
BGSAVE开始,所有写命令都会额外发送一份给该客户端 - 命令暂存在客户端输出缓冲区,待 RDB 发送完成后立即推送
✅ 保证全量同步期间的数据不丢失
4.2 复制积压缓冲区(Replication Backlog)
- 主节点维护一个固定长度的环形缓冲区(默认 1MB)
- 存储最近执行的写命令(RESP 格式)
- 用于后续部分重同步
配置项:
repl-backlog-size 1mb # 可根据网络稳定性调整(如 100mb)
五、阶段二:增量复制(PSYNC 成功)
5.1 核心机制:偏移量(offset) + 复制 ID(replid)
- master_repl_offset:主节点已发送的字节数(全局递增)
- slave_read_repl_offset:从节点已接收的字节数
- master_replid:主节点的唯一标识(40 字节十六进制字符串)
📌 只要 replid 相同 + offset 在 backlog 范围内 → 可部分重同步
5.2 PSYNC2 的突破:持久化 replid
Redis 4.0+ 将主节点的 replid 和 repl_offset 持久化到 RDB 文件末尾!
RDB 文件结构:
[REDIS][...数据...][EOF][aux: repl-id=abc123][aux: repl-offset=12345]
效果:
- 主节点重启后,从 RDB 中恢复
replid和offset - 从节点断连重连时,携带旧
replid和offset - 主节点发现
replid匹配 → 直接从 backlog 补发缺失命令!
✅ 彻底解决"主节点重启导致全量同步"的历史难题
六、PSYNC 命令交互详解
6.1 从节点发起同步
bash
# 首次连接 or 不知道主状态
PSYNC ? -1
# 断连重连(携带上次的 replid 和 offset)
PSYNC abc123 12345
6.2 主节点响应
| 响应 | 含义 | 后续动作 |
|---|---|---|
+FULLRESYNC <replid> <offset> |
需全量同步 | 从节点准备接收 RDB |
+CONTINUE |
可部分重同步 | 从节点准备接收增量命令 |
-ERR ... |
协议错误 | 降级到 SYNC(旧版) |
七、关键配置参数与调优
| 参数 | 默认值 | 说明 | 生产建议 |
|---|---|---|---|
repl-backlog-size |
1mb | 复制积压缓冲区大小 | 高延迟网络设为 100~500mb |
repl-timeout |
60s | 复制超时时间 | 根据网络质量调整 |
client-output-buffer-limit replica |
256mb | 从节点输出缓冲区限制 | 避免大 Key 导致主 OOM |
repl-diskless-sync |
no | 是否无盘复制 | SSD 环境可开启,减少磁盘 IO |
💡 无盘复制(Diskless Replication) :
RDB 直接通过 socket 发送给从节点,不落盘,适合高性能 SSD 环境。
八、主从同步的可靠性保障
8.1 心跳机制
- 从节点每秒发送
REPLCONF ACK <offset>给主节点 - 主节点据此更新
slave_ack_offset,用于:- 计算从节点延迟(
lag = master_offset - slave_ack_offset) min-replicas-to-write安全写入控制
- 计算从节点延迟(
8.2 安全写入(防脑裂)
# 至少有 1 个从节点在线且延迟 < 10 秒,才允许写入
min-replicas-to-write 1
min-replicas-max-lag 10
✅ 避免主节点网络分区时继续写入导致数据不一致
九、常见问题与排查
❓ Q1:为什么总是全量同步?
- 原因1 :
repl-backlog-size太小,断连期间数据被覆盖 - 原因2:主节点重启(旧版 Redis)
- 排查 :
INFO replication查看master_replid是否变化
❓ Q2:主从延迟(lag)很高?
- 可能原因 :
- 从节点磁盘慢(加载 RDB 慢)
- 网络带宽不足
- 主节点写入突增(大 Key、FLUSHALL)
- 优化 :增大
repl-backlog-size,升级从节点硬件
❓ Q3:从节点无法写入?
- 正常现象 :从节点默认
replica-read-only yes - 如需写 (如临时调试):
CONFIG SET replica-read-only no(重启失效)
十、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!