Redis 7/8 复制:全量与部分重同步、offset 与丢写窗口
主库返回 OK,从库为什么仍可能没有这次写入?Redis 7/8 默认异步复制,客户端成功、从库追上、数据落盘是三个不同事件。重同步解决断线后如何接续历史,不能单独保证切换后零丢写。
replid 与 offset:先确认历史,再比较进度
replid 标识一段数据集历史;offset 是该历史中复制流的字节位置,不是命令条数、键数量或时间戳。复制流还可能包含控制命令,不能把 offset 增量直接换算成业务写入数。同一个 offset 只有在对应历史相容时才有比较意义。
从库断线前已处理到位置 R,重连时用 PSYNC <replid> <R+1> 请求下一字节。主库先判断历史是否可接续,再判断缺失区间是否仍可提供;成功返回 CONTINUE,否则进入 FULLRESYNC。首次连接没有可用历史时,通常用 PSYNC ? -1 请求全量同步。
从库晋升主库时会生成新的 replid,同时保留旧历史的第二 ID 及有效 offset 边界。其他从库可能凭旧 ID 接续,但请求不能越过旧历史的有效边界。“主库换了就必然全量”和“offset 相同就能接续”都过度简化了判断。可对照官方 Redis 8.0 replication.c 中的 masterTryPartialResynchronization 与 shiftReplicationId。
backlog:保存有限历史,不是持久化日志
backlog 为近期复制流提供可回放窗口,不是每个从库独占一份的离线队列。假设当前历史保留字节范围为 [B, B+H),其中 H 是有效历史长度,则请求位置 P 必须满足 B <= P <= B+H;等于右边界表示没有缺失字节。还必须同时满足 replid 条件及第二 ID 的边界约束。
| 断线重连条件 | 结果与代价 |
|---|---|
| 历史相容,缺失字节仍在 backlog | 部分重同步,仅补缺口 |
| 历史相容,但缺口已被覆盖 | 全量重同步,重建数据集 |
| 历史无法识别,或 backlog 已释放 | 无法部分接续,转全量 |
规划容量时,可用“峰值复制字节率 × 允许断连时间 × 余量”估算。例如 2 MiB/s、断连 30 秒、两倍余量,约需 120 MiB。这是容量推导,不是实测保证;突发写入、重连耗时和真实历史保留量仍会改变结果。
repl-backlog-size 控制窗口大小;所有从库离线后,主库还可能按 repl-backlog-ttl 释放 backlog。调大窗口也不能修复历史不匹配。参数解释见官方 7.2 配置与 8.0 配置。
全量重同步:快照之后还要追平增量
全量并非传完 RDB 就结束。RDB 表示某个基准状态,生成、传输、加载期间的新写入仍需按复制流顺序补齐,才能接上持续复制。
按图阅读:首次连接建立快照基准,加载 RDB 后补齐复制流并 ACK;断线后携带历史与下一字节位置,条件满足时通过 CONTINUE 续传。
这是逻辑顺序示意,不要求所有版本在同一连接上依次传输。Redis 7 可通过磁盘 RDB 或无盘方式传快照;Redis 8.0 的实现还支持 RDB 与命令流并行传输,在从库缓冲命令流,加载完成后应用,见上述 8.0 配置中的 replica-full-sync-buffer-limit。两者都要遵守“基准状态加后续增量”的语义。
主库持续服务也不代表同步没有成本:快照生成、fork、写时复制、网络和缓冲区都会消耗资源。从库加载快照时的服务行为又与加载配置有关,不应承诺全程可读。慢从库反复被断开、反复全量,可能比短暂断连更危险。基础流程见官方复制说明。
异步复制留下怎样的丢写窗口
考虑如下故障时序:主库执行 SET order:42 paid,向客户端返回 OK;从库尚未处理该写入,主库失联;这个落后的从库被提升后,读取仍得到旧值。原主库恢复并跟随新主库时,会接受新历史,旧写入不会自动合并回来。backlog 的用途是补流,不是裁决两条分叉历史。
min-replicas-to-write 与 min-replicas-max-lag 能在健康从库不足时拒写,但依据的是从库连接及最近确认情况,不是逐条写入确认。配置为 1 和 2 秒,不等于“每笔写已复制一份”,也不保证严格两秒以内的业务损失上限。
WAIT 与 WAITAOF:把成功条件写清楚
以下是独立本地测试实例中、同一个交互连接的命令示意;未执行,不是采集输出。不能拆成两个独立 redis-cli 调用,否则 WAIT 不再关联原连接的写入。
redis-cli -h 127.0.0.1 -p 6379
SET order:42 paid
WAIT 1 1000
INFO replicationWAIT 等待本连接此前写入获得指定数量从库确认,返回实际确认数。预期失败分支:没有可用从库时,超时返回 0;这不会撤销已经执行的 SET。应用应区分“写失败”和“写已执行但确认不足”,重试非幂等操作前须处理重复执行风险。
WAIT 不要求 AOF 落盘,也不会把复制系统变成强一致系统。Redis 7.2 起可用 WAITAOF 等待本地及指定数量从库完成此前写入的 AOF fsync,例如启用相应 AOF 后用 WAITAOF 1 1 1000;返回的两个计数都要检查。Redis 7.0 没有此命令,相关节点也必须满足持久化条件,不能只看版本号。
排查时先看 INFO replication 的链路状态、当前及第二 replid、主从 offset、repl_backlog_first_byte_offset 和 repl_backlog_histlen,再判断缺口是否可补。字段含义见官方 INFO。offset 差表示字节差,不是精确延迟秒数;有复制确认,也仍需结合持久化和故障切换策略判断可接受的丢写风险。
继续阅读:原有 Redis 知识页、技术进阶阅读入口。
