Redis主从同步优化:环形缓冲区实现PSYNC增量复制
PSYNC增量复制退化为全量同步的根本原因是repl_backlog_buffer过小或从节点断开太久导致offset被覆盖,主节点返回FULLRESYNC;实操需调大repl-backlog-size至512MB~2GB,并通过日志“Partialresynchronizationrequestaccepted”确认增量生效。
PSYNC增量复制退化为全量同步的根本原因是repl_backlog_buffer过小或从节点断开太久导致offset被覆盖,主节点返回FULLRESYNC;实操需调大repl-backlog-size至512MB~2GB,并通过日志“Partial resynchronization request accepted”确认增量生效。

PSYNC 增量复制为什么有时退化成全量同步
根本原因是主节点的 repl_backlog_buffer(环形缓冲区)太小,或从节点断开太久,导致所需偏移量 offset 对应的数据已被覆盖。这时主节点只能返回 FULLRESYNC,触发 RDB 生成和传输。
实操建议:
repl-backlog-size默认只有 1MB,生产环境建议设为 512MB~2GB,具体看写入压力和网络恢复预期时长- 用
INFO replication查看repl_backlog_active、repl_backlog_size和repl_backlog_first_byte_offset,确认缓冲区是否活跃、是否被频繁覆盖 - 从节点重连时发的
PSYNC请求,若 offset 不在当前 backlog 范围内,主节点会拒绝增量,不报错但日志里有Unable to partial resync
如何验证 PSYNC 是否真正生效
不能只看 INFO 里的 master_repl_offset 和 slave_repl_offset 是否接近——它们可能只是“看起来同步了”,实际走的是全量。
实操建议:
- 在从节点执行
ROLE,返回结果中若含"state":"online"且"offset"持续增长,说明是增量;若刚连上就出现"state":"loading"并持续数秒以上,大概率正在加载 RDB - 主节点日志搜索
Partial resynchronization request accepted,这是唯一可靠信号;如果看到Starting BGSAVE for SYNC,说明已 fallback 到全量 - 监控
sync_partial_ok和sync_full_ok这两个INFO stats指标,长期观察比值,低于 0.8 就该调 backlog 了
repl-backlog-ttl 影响什么,设成 0 真的更好吗
repl-backlog-ttl 控制的是:当没有从节点连接时,环形缓冲区保留多久。不是“断开后还能等多久重连”,而是“空闲状态下 buffer 存多久”。设为 0 表示永不释放,但不会延长从节点断线后的可增量窗口。
实操建议:
- 设为 0 只在极少数场景有用——比如你明确知道从节点会规律性断连(如定时维护),且能保证断连间隔短于最大可能 offset 增长量
- 更关键的是
repl-backlog-size+ 实际写入 QPS,例如每秒写入 2MB,那 10 秒断连就需要至少 20MB backlog,ttl 再大也救不了 size 不够 - 注意:buffer 占用内存是常驻的,即使没从节点连着,
repl-backlog-size也会预分配,别盲目堆到 4GB 导致主节点 OOM
从节点重启后 PSYNC 失败的常见原因
从节点重启后 runID 必然变化,它只能用上次的 runID 发 PSYNC,主节点一查 runID 不匹配,直接拒绝,返回 NOAUTH 或 ERR(取决于是否开启认证),而不是你期待的 FULLRESYNC。
实操建议:
- 从节点配置必须启用
replica-announce-ip和replica-announce-port,否则主节点记录的地址可能失效,导致心跳失败、runID 关联丢失 - 确保从节点启用了
replica-read-only yes(默认),否则某些旧版本 Redis 在只读关闭时会清空复制元数据 - 检查主节点是否开启了
requirepass,而从节点没配masterauth——此时 PSYNC 请求会被认证拦截,日志只显示Client closed connection,容易误判为网络问题
环形缓冲区本身不复杂,难的是把 offset、runID、repl_backlog_first_byte_offset 这三者的时间关系理清楚;很多人卡在“明明 backlog 没满,却还是全量”,其实是因为从节点报的 offset 主节点压根不认——runID 对不上,连查 buffer 的机会都没有。


































