土法炼钢兴趣小组的算法知识备份

【Redis / 缓存内核】复制:PSYNC · backlog · 副本只读与读己之写

文章导航

分类入口
databasestorage
标签入口
#redis#replication#psync#backlog#replica-read-only#read-your-writes#async-replication#7.4

目录

主库写完一条 SET 后立刻把连接切到从库读,偶尔能读到旧值——这不是客户端 bug,而是 Redis 复制默认异步 的必然结果。混合持久化 钉住了单机崩溃窗口;复制回答的是 多副本之间 数据何时对齐。运维里「从库分担读流量」与开发里「写后立刻读」若不在同一语义层讨论,故障复盘往往变成互相甩锅。

本文是「Redis / 缓存内核」系列第 12 篇(共 16 篇)。→ 系列目录

版本锚定:Redis OSS 7.4 / 8.x;源码路径以 src/replication.c 为准;配置见 redis.confREPLICATION 段。


一、问题从哪来:异步复制不是「慢一点的同步」

经典主从复制模型里,主节点先本地提交写,再把变更流推给副本;副本应用流的速度可以落后主节点任意时长(网络抖动、副本 CPU、大 key 回放)。这与 Gray 在 Notes on Database Operating Systems(1978)里归纳的 primary-backup 异步复制 一脉相承:主库不等待副本确认即向客户端返回成功——复制延迟是设计特性,不是实现瑕疵

Redis 官方 Replication 文档写明:副本通过 PSYNC 携带旧 replication ID 与已处理 offset,主库尽量只发增量;若 backlog 不足或 ID 未知,则 全量重同步FULLRESYNC,通常走 RDB 快照流)。SYNC 为旧协议,不支持部分重同步,新实例以 PSYNC 为主。

缓存架构 的 Cache-Aside 对照:应用「写 DB + 删缓存」假设缓存 miss 后读到的是 DB 真值;若读路径改走 滞后副本,等价于在缓存与 DB 之间又插了一层 可能陈旧的只读视图,击穿/雪崩之外又多了一维 复制滞后


二、复制数据流与 replication.c 锚点

主库每执行一条会改变数据集的命令,经 replicationFeedSlaves() 把 RESP 命令流 fan-out 到所有已连接副本,并同时写入主库的 replication backlog(环形缓冲)。副本侧 syncWithMaster() 驱动握手与流式消费。

sequenceDiagram
  participant Client
  participant Master
  participant Backlog
  participant Replica
  Client->>Master: SET k v
  Master->>Master: apply command
  Master->>Backlog: append repl stream
  Master->>Replica: propagate command
  Master-->>Client: OK
  Note over Replica: apply may lag behind OK
  Replica-->>Client: GET k (optional read path)
函数 / 命令 文件 作用
replicationFeedSlaves() replication.c 主库向副本列表广播命令流并写 backlog
addReplyReplicationBacklog() replication.c 部分重同步时从 backlog 按 offset 回放
syncCommand() replication.c 处理 SYNC / PSYNC 握手
syncWithMaster() replication.c 副本侧连接主库、解析 +CONTINUE / +FULLRESYNC
readSyncBulkPayload() replication.c 全量同步时接收 RDB 或 diskless 流

PSYNC 握手结果在源码中分为 PSYNC_CONTINUE(增量继续)、PSYNC_FULLRESYNC(全量)、PSYNC_NOT_SUPPORTED(回退 SYNC)。主库回复 +CONTINUE 时副本从已知 offset 追赶;+FULLRESYNC <replid> <offset> 则触发新一轮快照传输——对大实例,全量同步的 fork + 网络 + 加载 成本远高于追 backlog。


三、Replication backlog:部分重同步的内存账

redis.conf 默认 repl-backlog-size 1mb:主库维护固定大小环形缓冲,记录最近 replication 字节流。副本断连重连后若请求的 offset 仍在环内,主库只补发缺口;若已被覆盖或 replid 不匹配(主库重启),则 FULLRESYNC

工程上 backlog 大小的粗算(官方注释与运维文档共识,非性能 benchmark):

\[ \text{backlog\_size} \gtrsim \text{峰值写带宽(字节/秒)} \times \text{可容忍断连时长(秒)} \]

配置项 默认(7.4 redis.conf 含义
repl-backlog-size 1mb 环形缓冲容量;有至少一个副本连接时才分配
repl-backlog-ttl 3600 最后一个副本断开后,经过该秒数释放 backlog;0 表示永不释放
repl-diskless-sync 依版本与构建 全量同步是否经 socket 直传 RDB,减轻磁盘尖刺

INFO replication 中的 sync_partial_ok / sync_partial_err 可观察部分重同步成败频率——sync_partial_err 持续升高通常意味着 backlog 相对写速率过小,而非网络「偶发」可忽略。

Redis 4.0+ 还支持 failover 后新主用 旧主的 replid 片段 服务仍记得旧 ID 的副本(官方 Partial sync after restarts and failovers),降低 Sentinel/手动故障转移后的全量同步风暴。这与「主库重启必全量」的早期印象不同,但 不能 推导出「任意拓扑下都不会 FULLRESYNC」。


四、副本只读与读路径语义

replica-read-only yes(默认)时,副本拒绝非主从复制来源的写命令(READONLY 错误)。副本仍可执行部分管理类命令,但 业务写必须走主库

读副本时的关键配置与行为:

机制 说明
replica-serve-stale-data yes(默认) 主从断连期间副本仍可服务读,数据可能陈旧
replica-read-only 阻止客户端误写副本
WAIT 客户端可阻塞等待至少 N 个副本确认 offset(非默认路径,有延迟代价)
min-replicas-to-write / min-replicas-max-lag 主库侧写入门禁:副本不足或滞后过大时拒绝写

读己之写(read-your-writes):同一会话写主库后读副本,Redis 默认不保证。Terry et al. Session Guarantees for Weakly Consistent Replicated Data(PDIS 1994)把 session 级单调读/写己之写等形式化;Redis 复制协议 实现 session stickiness 或因果元数据,应用若需要 RYW,必须:写后读主同连接读主客户端跟踪 repl offset 并等待副本追上(复杂且少见),或接受陈旧读。

这与 PACELC 框架(Abadi, IEEE Computer 2012)在「分区不发生」时的 延迟—一致性 权衡一致:把读流量挪到副本是在用 staleness主库 CPU/连接,不是免费午餐。


五、全量同步、diskless 与持久化交叉

全量重同步路径与 RDB 共享 fork() 子进程生成快照语义:大 used_memory 时 COW 压力与 fork 延迟 会同时打击主库(见第 15 篇)。repl-diskless-syncrepl-diskless-load(版本相关)改变 RDB 是经磁盘还是 socket 管道,不改变「全量比增量昂贵」的数量级判断。

副本加载 RDB 期间同样阻塞对外服务(除非配置允许 stale)。运维上应把 避免 FULLRESYNC 当作容量规划项:稳定网络、足够 backlog、控制主库频繁重启、Sentinel 故障转移后观察 master_link_status


六、常见误解

误解 事实
「有从库就不会丢写」 异步复制下主库 OK 只表示主库已应用;主库宕机且未同步到副本的写会丢,除非用 WAIT 或同步复制产品特性
「PSYNC 总能增量」 backlog 环覆盖或 replid 变化即 FULLRESYNC;写带宽高而 repl-backlog-size 仍为 1mb 时极易失败
「从库只读就不会不一致」 只读指不能写;读到的仍是滞后视图,与主库之间无线性一致性
「复制等于持久化」 复制解决的是 多副本可用性;副本内存里的数据同样会在全集群故障时丢失,除非另有 RDB/AOF(第 9–11 篇)
「读写分离不用改代码」 写后读若打到副本,需应用层路由或接受陈旧;Cache-Aside 删缓存也不能修复副本滞后

七、学术谱系、争论与开放问题

谱系:Gray 主备异步复制 → Redis PSYNC(Salvatore Sanfilippo 设计,官方文档与 replication.c)→ 4.0+ failover 部分同步 → 7.x/8.x diskless 与 IO 线程复制侧优化(细节见 release notes,本篇不逐 commit 展开)。

争论

  1. 异步复制 vs 同步确认WAIT 把复制推进客户端延迟路径,提升「已复制」语义但降低可用性与尾延迟;与 Dynamo 系「客户端可调 QUORUM」类似,Redis 选择默认异步以保主库低延迟。
  2. 读副本是否值得:副本滞后在写多读少、强 RYW 场景下可能不如直接扩主库或加本地缓存;读副本适合 可容忍陈旧 的只读聚合(计数、画像),不适合 写后立刻读 的会话状态。

开放问题


八、运维检查清单(无伪造输出)

在已有 redis-server 的环境按序执行;数值以你机器为准:

# 复制拓扑与滞后
redis-cli INFO replication
redis-cli ROLE

# 部分重同步成败(sync_partial_err 持续涨 → 加大 backlog)
redis-cli INFO stats | egrep 'sync_partial'

# 运行时调 backlog(需评估内存)
redis-cli CONFIG GET repl-backlog-size
redis-cli CONFIG GET repl-backlog-ttl

# 副本只读与陈旧读
redis-cli CONFIG GET replica-read-only
redis-cli CONFIG GET replica-serve-stale-data

判读要点:master_repl_offsetslave_repl_offset 差值即滞后字节量;master_link_status:down 时若仍 replica-serve-stale-data yes,读路径可能返回 断连前 的视图。写后读会话应 显式路由到主库,不要假设负载均衡「轮询即可」。


九、与 Cluster 的衔接

单主复制解决 读扩展与故障转移(配合 Sentinel,第 16 篇),不解决 写扩展。数据量或写 QPS 超过单机时进入 Cluster 分片:复制在 每个 hash slot 的主从对 上仍成立,但多 key 事务与读路由规则被 slot 约束改写。


十、小结

三句话小结

  1. Redis 复制默认 异步:主库 OK 不蕴含副本已应用,读副本可能陈旧,与 Cache-Aside 的「删缓存后读」不是同一层保证。
  2. PSYNC + replication backlog 把短暂断连从「全量 RDB」降为「追增量」,backlog 需按写带宽×断连时长规划,sync_partial_err 是过小信号。
  3. replica-read-only 只防误写,提供读己之写;要强会话语义应写后读主,或显式 WAIT 并承担延迟。

下一篇进入分片:Cluster:16384 slot · MOVED/ASK


参考资料

规范与源码(A 级)

  1. Redis Documentation, ReplicationPSYNCFULLRESYNC、部分同步与 failover)。
  2. Redis OSS 7.4 redis.confREPLICATION 段(repl-backlog-sizerepl-backlog-ttlreplica-read-only)。
  3. Redis OSS 7.4 src/replication.creplicationFeedSlavesaddReplyReplicationBacklogsyncCommandsyncWithMaster

论文与经典文献(A 级)

  1. Gray, J., Notes on Database Operating Systems,1978(主备复制与异步提交模型)。
  2. Terry, D. B. et al., Session Guarantees for Weakly Consistent Replicated Data, PDIS 1994(session / read-your-writes 形式化)。

辅助(B 级)

  1. Abadi, D. J., Consistency Tradeoffs in Modern Distributed Database System Design, IEEE Computer 2012(PACELC;对照延迟—一致性直觉)。
  2. 混合持久化缓存架构

返回 系列目录 | 上一篇:混合持久化 | 下一篇:Cluster

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。


By .