适用场景

Redis 采用主从复制,业务高峰时偶发写入延迟、主库 CPU 或网络突刺。日志中反复出现 Full resync requestedPartial resynchronization not accepted,从库短时间内加载 RDB,甚至与主库断开后很久才恢复。

本文适用于 Redis 67 的单主多从架构。命令默认在 Redis 所在主机执行;生产环境请使用具备最小权限的账号,并避开业务高峰调整配置。

现象描述

典型的时间线如下:从库发生几十秒网络抖动或进程重启,本应使用增量复制追上主库,却触发主库生成 RDB 并向从库全量传输。全量同步期间,主库的 fork、磁盘和网卡流量显著上升;从库载入 RDB 时无法及时提供数据,读流量可能被转移到主库。

主库日志通常可看到:

* Partial resynchronization not accepted: Replication ID mismatch or backlog is too small
* Starting BGSAVE for SYNC with target: replicas sockets

从库日志则可能出现:

* Full resync from master: <runid>:123456789
* MASTER <-> REPLICA sync: receiving streamed RDB from master

原理:为什么短暂断连会变成全量同步

主库把最近写命令保存在环形的复制积压缓冲区(repl-backlog)中。从库断开时会记录已处理的复制偏移量;重连后,如果该偏移量仍在主库 backlog 覆盖的范围内,就只需接收缺失命令(部分重同步,PSYNC)。

如果断连期间产生的复制数据超过 repl-backlog-size,旧数据被环形缓冲区覆盖,主库无法补齐缺口,只能全量同步。注意 backlog 的容量以复制字节数计算,而不是 QPS;大 value、批量写入和 AOF 重写期间的流量都会加速覆盖。

排查步骤

1. 确认全量同步和 backlog 覆盖是否发生

在主库执行:

redis-cli INFO replication
redis-cli INFO stats | egrep 'sync_full|sync_partial|rejected_connections'

重点字段:

  • sync_full:Redis 启动以来的全量同步次数;短时间持续增长需要处理。
  • sync_partial_ok / sync_partial_err:部分重同步的成功和失败次数。
  • repl_backlog_size:当前 backlog 配置容量。
  • repl_backlog_histlen:当前实际保留的复制历史长度;接近 repl_backlog_size 时说明缓冲区已被写满。
  • master_repl_offset:主库最新复制偏移量,可配合从库偏移量评估落后量。

再检查近期开关连接和同步日志:

journalctl -u redis-server --since '2 hours ago' \
  | egrep -i 'full resync|partial resynchronization|connection with replica'

若 Redis 不由 systemd 管理,请改为检索实际 logfile。不要只看某一次 sync_full:Redis 重启后的首次同步属于预期,关键是正常运行期间是否重复出现。

2. 计算所需 backlog 容量

先记录主库复制偏移量,间隔 60 秒再记录一次:

redis-cli INFO replication | grep '^master_repl_offset:'
sleep 60
redis-cli INFO replication | grep '^master_repl_offset:'

两次 offset 的差值约为每分钟复制量。例如 60 秒增长 180 MiB,且监控显示从库最长可能中断 5 分钟,则理论最低值为:

180 MiB/min × 5 min = 900 MiB

再加 50%~100% 余量,应将 repl-backlog-size 设为 1.5G 或 2G。余量用于应对写入突刺、重连抖动和估算误差。若没有监控数据,先用高峰期的 offset 增量测量,不能仅按内存大小猜测。

3. 排除“容量足够但仍无法部分同步”的原因

执行:

redis-cli INFO replication
redis-cli CONFIG GET repl-backlog-size repl-backlog-ttl repl-diskless-sync
redis-cli -h <replica-host> -p 6379 INFO replication

确认从库的 master_replid 与主库 master_replid 对应,且从库不是被重建后带着错误数据目录启动。还要检查:

  • 主库是否重启或发生故障切换;复制 ID 改变时旧 offset 不能继续使用。
  • repl-backlog-ttl 是否过小;所有从库断开超过该时间后,主库会释放 backlog,下一台从库必然全量同步。
  • 从库网络是否发生丢包、NAT 空闲回收或连接超时;这些问题会让容量调整只能“缓解”,不能根治。

修复方案

1. 按测量结果扩大 backlog

以下示例把 backlog 调整到 2 GiB,并使配置在重启后生效:

redis-cli CONFIG SET repl-backlog-size 2gb
redis-cli CONFIG REWRITE
redis-cli CONFIG GET repl-backlog-size

CONFIG SET 立即生效,CONFIG REWRITE 将当前配置写回配置文件。执行前务必确认主机有足够的可用内存:backlog 是主库额外常驻内存,不能依赖 swap。托管 Redis 或配置文件被自动化系统管理时,应通过对应平台或配置管理工具修改,避免下次发布被覆盖。

2. 为短暂的全从库失联保留 backlog

如果维护窗口、网络切换时可能让所有从库同时离线,可适度延长保留时间:

redis-cli CONFIG SET repl-backlog-ttl 3600
redis-cli CONFIG REWRITE

repl-backlog-ttl 只在没有任何从库连接后开始计时,单位为秒。设置为 0 代表永不释放,适合内存余量充足且要求较高的场景;通常设置一个覆盖维护窗口的有限值更稳妥。

3. 降低全量同步的冲击

无法避免首次全量同步时,可限制其连锁影响:为从库设置合理的 client-output-buffer-limit replica,保证备份磁盘与网络容量;监控 latest_fork_usecrdb_bgsave_in_progress、网卡带宽和从库加载耗时。不要用频繁重启从库来“修复复制”,这会反复触发更重的全量传输。

定位示例

某主库高峰期复制偏移量约 220 MiB/min,原配置为 repl-backlog-size 256mb。一次 90 秒的交换机抖动产生约 330 MiB 写入,已经超过 backlog,3 台从库先后全量同步。将 backlog 调整为 1G 后,同类 2 分钟抖动仅使 sync_partial_ok 增加,sync_full 不再增长。

这个案例的关键不是“把参数调大”,而是用 offset 增量将参数和可接受的断连时长关联起来;这样容量会随业务写入增长被重新评估。

预防措施

  • sync_full 增量、sync_partial_err、从库 master_link_down_since_secondsrepl_backlog_histlen / repl_backlog_size 建立告警。
  • 每次大促、批量导入或 value 结构变更后,重测高峰复制速率并复核 backlog。
  • 将从库重启、网络切换和故障演练安排在可观测窗口,记录是否走部分重同步。
  • 避免全部从库共用同一网络故障域;至少保留一个跨故障域副本,降低同时断连的概率。
  • 在容量评审中把主库 fork 内存、backlog、客户端输出缓冲区一起纳入内存预算。

总结

频繁全量同步通常是复制缺口超过 backlog 覆盖范围的结果。通过 master_repl_offset 测量真实复制速率,以最长可接受断连时间计算容量,再结合网络稳定性和 backlog 保留时间治理,能让短暂抖动恢复为轻量的部分重同步,避免它演变为主从同时承压的线上事件。