Redis 复制:从全量同步到 PSYNC2

Sentinel 可以在主节点故障后提升副本,Redis Cluster 也能在故障分片内完成主从切换。两种架构都要求副本在故障发生前持续接收主节点的数据,并尽量跟上写入进度。Redis 复制负责维护这份可供接管的数据。

但副本不是主节点的实时镜像。Redis 默认异步传输复制流,主节点响应写入时,副本可能还没有收到对应的数据。链路断开后能否从断点接着传,则取决于 replication ID、offset 和 replication backlog 能否证明双方共享同一段复制历史。本文从部分重同步、全量同步和正常复制流三个阶段入手,解释这些机制为什么存在,以及它们在恢复成本、内存占用、写入延迟和数据一致性之间的取舍。

1. 为什么需要复制#

对现代在线业务来说,只运行一个 Redis 进程、把所有数据和流量压在一台机器上,已经很难满足生产要求。节点故障时,业务需要尽快恢复服务;当数据量或访问量超过单机上限时,又需要把容量和请求分散到多个节点。前者是高可用问题,后者是大数据量和高并发带来的水平扩展问题。RDB 和 AOF 只能帮助单个节点重启后恢复数据,不能让另一个在线节点立即接管服务,也不能突破单机容量。

Redis 提供了两种典型部署架构来处理这两类问题。Sentinel 面向不分片的数据集,监控主节点和副本,并在主节点故障后自动选择副本接管,主要解决高可用。Redis Cluster 把键空间分到多个主节点,用分片扩展容量和写入吞吐;每个分片内仍配置副本,因此 Cluster 也包含分片级的高可用。

两种架构都依赖复制。无论是 Sentinel 提升副本,还是 Cluster 在故障分片内切换主节点,候选节点都必须预先拥有尽可能新的数据。复制持续把主节点的数据变更传给副本,是两种架构的共同基础。它不负责故障判断、选主或客户端路由,这些职责分别由 Sentinel、Cluster 和客户端完成。

2. Redis 如何复制数据#

Redis 复制的发起方是副本。它连接主节点,完成 PING、可选的身份认证和 REPLCONF 信息交换,然后发送 PSYNC <replication-id> <offset> 申报自己已有的复制历史和进度。主节点维护 replication ID、当前 offset 和 replication backlog,并根据副本的请求选择同步方式。

复制链路按主节点的处理顺序分为三段:副本断线恢复时,先尝试部分重同步;第一次复制或部分重同步条件不成立时,改为全量同步;副本追上主节点后,进入持续传输复制流的正常运行状态。这三段并不是每次都依次执行:部分重同步成功后会跳过全量同步,直接回到复制流。两种同步方式都通向同一个结果:先让副本追上,再持续传递新的数据变更。

为什么复制进度要同时使用 replication ID 和 offset?

offset 只是某一条复制历史中的字节位置,并不是全局版本号。两条不同的复制历史即使 offset 相同,对应的数据也可能完全不同。replication ID 用来标识复制历史,offset 用来标识这条历史中的位置;主节点只有同时核对二者,才能判断副本缺少的是不是自己仍可补发的那段字节。

PSYNC2 还保留 secondary replication ID 和它的有效 offset 边界,使新主节点在故障转移后仍能识别上一条复制历史。Redis 只保存当前和上一代两个 ID,不追踪完整的历史树,元数据大小固定,判断也简单。连续多次角色切换或缺失数据已经离开 backlog 时,仍需全量同步。

2.1 部分重同步:恢复断开的链路#

复制连接断开后,副本保留上次的 replication ID 和下一个需要的 offset,重连时用 PSYNC <replication-id> <offset> 提交给主节点。主节点按 PSYNC2 规则检查两个条件:

  1. 请求 ID 等于 current replication ID,或者等于 secondary replication ID 且请求 offset <= second_repl_offset
  2. 从请求 offset 到主节点当前 offset 的所有字节仍完整保存在 replication backlog 中。

两个条件都满足时,主节点返回 +CONTINUE,从请求 offset 开始发送缺失字节;副本按顺序应用这段复制流,追上后直接进入第 2.3 节的持续复制。任一条件不满足时,主节点都无法证明副本可以从现有历史继续,因此返回 +FULLRESYNC,转入第 2.2 节。当前 ID 不一致并不必然触发全量同步,PSYNC2 保留的 secondary ID 是故障转移后仍可能接续旧历史的例外。

图中的副本已处理到 10086,下一个需要的字节是 10087,因此请求为 PSYNC repl-A 10087。主节点当前 offset 是 10119,需要传输 10087 到 10119,共 10119 - 10087 + 1 = 33 字节。backlog 从 10085 开始,完整覆盖这段范围;只要 ID 条件也成立,主节点就能执行部分重同步。

副本携带 replication ID 和 offset 重连,主节点先识别复制历史,再检查缺失区间是否仍在 replication backlog,从而选择部分重同步或全量同步
复制历史可识别且缺失数据仍在 backlog 时,主节点才返回 +CONTINUE。

backlog 是定长环形缓冲区,而不是无限增长或持久化的命令日志。这样可以把内存消耗限制在固定范围内,并按 offset 回放连续字节;取舍是只能覆盖最近一段历史。写入速度越高、断线越久,旧字节就越容易被覆盖。backlog 容量除以每秒复制流量,大致就是它能容纳的断线时长。

2.2 全量同步:首次复制或无法部分重同步#

副本第一次连接主节点时没有可复用的复制历史,通常发送 PSYNC ? -1。断线重连时,如果 replication ID 无法识别,或者缺失字节已离开 backlog,部分重同步也无法执行。这两类情况下,主节点返回 +FULLRESYNC <replication-id> <offset>,用全量同步为副本重新建立数据基线。

全量同步按下面的顺序执行:

  1. 主节点生成某一时刻的 RDB 快照,同时缓冲快照生成和传输期间的新写入。
  2. 主节点把 RDB 发送给副本;副本接收并加载快照,替换自己的旧数据集。
  3. 主节点发送同步期间缓冲的增量数据,副本应用完成后进入持续复制。

“快照 + 增量”给副本提供了确定的数据基线,主节点不必永久保存从启动以来的所有写命令,生成快照期间也可以继续处理请求。代价是 fork、写时复制、增量缓冲和 RDB 传输会消耗 CPU、内存、磁盘及网络资源;副本加载 RDB 时也可能暂时不能提供服务。diskless replication 可以省去主节点的中间 RDB 文件,但不会消除生成快照、占用网络和副本加载数据集的成本。

2.3 复制流:正常运行时的持续同步#

副本追上后,主节点把客户端写入以及过期、淘汰产生的数据变更编码进复制流,持续发送给副本,并按复制流的字节数推进 offset。即使当前没有副本连接,offset 也会继续增长;启用 backlog 时,主节点还会保留一段近期复制流,供断线后回放。

副本顺序应用复制流,并定期 ACK 自己已处理的 offset。复制链路没有固定的结束步骤:只有取消复制关系时,副本才会停止跟随;连接断开则重新回到第 2.1 节的同步判定。RDB、AOF 是否开启以及采用什么刷盘策略,是各节点自己的持久化配置,与复制 ACK 的时机相互独立。

这种异步设计避免了让每次写入的延迟和可用性都受副本影响。主节点在本地执行完命令后即可响应客户端,副本在后台追赶;慢副本或短时断线不会立即阻塞主节点写入。取舍是“客户端收到成功”与“副本已经收到数据”之间存在时间窗口。

客户端写入主节点后先收到成功响应,主节点随后把复制流发送给副本,副本处理后再返回 ACK,说明 Redis 默认复制是异步的
主节点通常不会等待副本 ACK 再响应客户端。

最典型的数据丢失过程是:主节点执行写入并向客户端返回 OK;副本还没收到对应的复制流;主节点随即故障;故障转移把尚未包含该写入的副本提升为主节点。客户端已经收到成功响应,这次写入却不在新主节点上。

副本读取也可能滞后。需要 read-after-write 或 write-after-read 语义时,客户端路由或业务协议必须明确哪些请求固定访问主节点,或者如何携带版本并等待副本追上。仅把读取切到副本无法提供这些保证。

WAIT numreplicas timeout 把更强的复制确认变成按请求选择,而不是让所有写入都承担副本延迟。它等待指定数量的副本 ACK 当前连接此前写入所到达的 offset,直到数量满足或超时,然后返回已经到达该 offset 的副本数。写入与 WAIT 必须在同一条客户端连接中执行。要求的副本越多、等待时间越长,数据落在故障窗口内的概率越小,写入延迟和超时概率也越高。

超时不会回滚已经接受的写入。WAIT 也不会把 Redis 变成 CP 系统或强一致系统,更不代表数据已经在副本磁盘上持久化;它确认的是复制 ACK,不是持久化完成。

min-replicas-to-writemin-replicas-max-lag 提供另一层约束:当近期通信正常的副本数量不足时,主节点拒绝写入,以限制主节点孤立或复制严重落后期间的风险。这个门槛不逐条等待写入持久化,仍不能保证故障转移零丢失。

3. 复制机制的演进#

Redis 复制的演进可以按 FULLSYNC → PSYNC1 → PSYNC2 理解。后两代方案没有取代全量同步,而是逐步扩大“可以避免全量同步”的范围;部分重同步条件不满足时,当前 Redis 仍会回到全量同步。这里的 FULLSYNC 只是对全量同步阶段的简称,不是 Redis 命令名。

Redis 2.8 以前:SYNC 全量同步#

Redis 2.8 以前,副本使用 SYNC 请求同步,协议不支持断点续传。Redis 2.8 及以后改用 PSYNC;副本第一次连接,或者无法部分重同步时,主节点返回 +FULLRESYNC <replication-id> <offset>。两者使用的协议不同,但都需要传输完整数据集。

这种方案不需要信任副本的旧数据,总能重新建立一份确定的数据基线。但是复制成本由整个数据集的大小决定,与副本实际缺少多少数据无关。

这一阶段的问题在断线后尤其明显。即使副本只丢失了几秒的数据,SYNC 也要重新生成和传输完整 RDB。网络不稳定时,反复断开就会触发反复全量同步,恢复时间被数据集大小决定,主节点还可能周期性出现延迟和内存抖动。diskless replication 可以省去中间 RDB 文件的磁盘 I/O,但不会消除生成快照、占用网络和加载数据集的成本。

Redis 2.8:PSYNC1 与断线续传#

Redis 2.8 引入第一版 PSYNC,后来通常称为 PSYNC1。为了支持断点续传,主节点维护持续递增的复制 offset,并在内存中增加一个定长的 replication backlog buffer(复制积压缓冲区),保存最近发送过的复制流;副本则记住主节点的运行 ID 和自己下一步需要的 offset。

副本重连时发送 PSYNC <run-id> <offset>。如果运行 ID 仍指向同一个主节点,而且请求 offset 之后的字节还在 backlog 中,主节点返回 +CONTINUE,只补发断线期间缺失的部分;否则返回 +FULLRESYNC。短暂网络抖动不再必然触发 RDB 生成和全量传输,复制恢复的耗时从“数据集有多大”缩小为“断线期间漏了多少数据”。

PSYNC1 的 runid 绑定 Redis 进程,副本记住的上游 runid 和 offset 也只保存在内存中。因此,以下场景都会让原有 runid 对断点续传失效:

  1. 主节点进程重启。 无论是崩溃后拉起、主机重启还是滚动升级,新进程都会生成新 runid。即使数据从同一份 RDB 或 AOF 恢复,副本携带的旧 runid 也无法匹配。
  2. 副本进程重启。 PSYNC1 没有把上游 runid 和已处理 offset 写入 RDB。副本重启后即使恢复了数据集,也没有可用的复制进度,只能请求全量同步。
  3. 副本被提升为主节点。 Sentinel 或 Cluster 故障转移,以及手工执行 SLAVEOF NO ONE,都会让提升后的节点以自己的 runid 提供复制。其他副本仍携带旧主节点的 runid,新主节点无法用 PSYNC1 证明自己继承了这段历史。
  4. 副本改挂到另一个主节点。 执行 SLAVEOF <host> <port> 切换上游时,副本只能保留当前一份上游复制状态。之后再切回原主节点,无法依靠已被丢弃的 runid 和 offset 续传。

这里的“丢失”不一定是字符串从内存中消失,也包括新主节点不再能用它识别共同历史。普通网络断开不会改变 runid;backlog 被覆盖也不会改变 runid,只是让所需 offset 离开了可回放窗口。两者都会使部分重同步失败,原因不同。

PSYNC1 因此仍有两类边界:复制历史因 runid 变化或状态丢失而无法识别,或者历史虽然可识别,但定长 backlog 已经覆盖所需字节。任一情况都会回到全量同步。

Redis 4.0:PSYNC2 如何补上 PSYNC1 的缺口#

PSYNC2 延续了 PSYNC1 的 backlog 判断,针对上面前三种“复制历史无法识别”的情况增加了三条恢复路径。

改进一:主节点重启后仍能识别旧历史#

PSYNC1 的 runid 随进程重启而改变,副本因此无法用旧 ID 续传。主节点有有效 backlog 时,PSYNC2 会把 replication ID 和 offset 写入 RDB。主节点从这份 RDB 重启后,把旧 ID 作为 secondary ID,并生成新的 current ID。停机前已处理到该 offset 的副本可以携带旧 ID 请求续传;新 backlog 仍覆盖请求位置时,便可部分重同步。

改进二:副本重启后恢复复制进度#

PSYNC1 只在内存中保存副本的上游 ID 和 offset,重启后这些进度全部丢失。PSYNC2 把它们写入 RDB;副本从包含这些元数据的 RDB 恢复时,可以继续用旧 ID 和 offset 发起 PSYNC。主节点仍能识别该 ID,且缺失字节还在 backlog 中时,副本无需重新加载整份数据。仅使用 AOF 恢复的副本不能依赖这条路径。

改进三:故障转移后接续旧历史#

PSYNC1 只识别一个 runid。副本被提升后,其他副本所持的旧 ID 会与新主节点不匹配。PSYNC2 改为同时保留 current replication ID 和 secondary replication ID。INFO replication 中的对应字段如下:

master_replid:a4e75e65a3bc4ea6860557e54919a0349585fedd
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:906412
second_repl_offset:-1

master_replidmaster_repl_offset 表示当前历史的 ID 和字节位置;master_replid2second_repl_offset 保存上一代 ID 及其有效上界。示例中的全零 ID 和 -1 表示当前没有有效的上一代历史。

如果副本复制到 906412 后被提升,Redis 会生成新的 current ID,把 a4e75e... 移入 master_replid2,并将 second_repl_offset 设为 906413。这里加一,是因为 PSYNC 传入的是下一个尚未收到的字节位置。其他副本可以用 PSYNC a4e75e... 906413 请求续传;backlog 仍覆盖所需字节时,新主节点返回 +CONTINUE。新 current ID 则用来区分故障转移后的新分支,避免与网络分区中继续写入的旧主节点混淆。

PSYNC2 仍只保存一代旧 ID,backlog 也仍是有限窗口。连续多次角色切换、在多个上游之间来回改挂、请求 offset 越界,或者缺失字节已被 backlog 覆盖时,仍需全量同步。

4. 扩展与生产实践#

4.1 Sentinel 与 Redis Cluster#

单条复制连接只能反映主节点与某个副本之间的通信状态,无法在网络分区时独立判断哪一侧应该继续提供写服务。故障检测、选主和客户端改路需要结合整个拓扑及多个节点的判断,因此 Redis 把这些职责交给 Sentinel 或 Cluster。代价是系统增加了协调节点、拓扑状态和客户端适配,不再只是启动一个主节点和一个副本。

Sentinel 不做分片,每个数据节点保存完整数据集。它监控主节点和副本节点,在确认故障后选出一个符合晋升条件的副本并提升为新主节点;客户端需要支持 Sentinel 服务发现,在故障转移后重新获取主节点地址并重连。

Redis Cluster 把键空间划分为 16384 个 hash slots。每个分片有自己的主节点和副本,故障转移发生在分片内部:图中只有故障的分片 B 切换到它的副本,其他分片不随之切换。cluster-aware 客户端负责 slot 路由,并处理 MOVEDASK 重定向。

维度 Sentinel Redis Cluster
数据布局 不分片,每个数据节点保存完整数据集 16384 个 hash slots 分布到多个分片
故障转移范围 整个数据集的主节点与副本节点之间切换 只在故障分片内提升副本
扩展能力 可扩展读吞吐,单份数据仍受单节点容量约束 通过分片扩展容量与吞吐
客户端行为 使用 Sentinel 感知的发现与重连 感知 slot 路由,处理 MOVEDASK
异步复制风险 已确认写入仍可能落在复制延迟窗口内 每个分片都保留相同的复制延迟窗口
Sentinel 在保存完整数据集的主从节点间切换主节点,Redis Cluster 则只在发生故障的哈希槽分片内提升副本
Sentinel 解决单数据集故障转移;Redis Cluster 同时提供分片与分片级故障转移。

两种方案都沿用异步复制。它们缩短故障后的恢复时间,却没有消除主节点已响应、副本尚未收到数据的窗口;图中的切换关系也不表示故障转移可以做到零数据丢失。

4.2 复制相关配置#

这一节涉及的参数先全部列在下面,行尾注释只说明直接作用。这些 # 注释用于阅读,不能原样放进 redis.conf;实际配置时需要删掉行尾说明,或改成独立注释行。

replicaof 10.0.0.10 6379       # 指定副本要连接的主节点地址和端口
masteruser replication         # 使用主节点上专用于复制的 ACL 用户
masterauth <password>          # 提供复制用户的密码,避免使用默认用户凭据
replica-read-only yes          # 拒绝客户端直接写副本,避免产生不会回传的本地数据
replica-serve-stale-data yes   # 主从断线或同步期间,仍允许副本返回可能过期的数据
replica-priority 100           # 供 Sentinel 选择晋升节点;设为 0 表示不参与晋升
repl-ping-replica-period 10    # 主节点每 10 秒向副本发送一次心跳

repl-backlog-size 256mb        # 在主节点内存中保留 256 MB 近期复制流
repl-backlog-ttl 3600          # 无副本连接 3600 秒后释放 backlog
repl-diskless-sync yes         # 全量同步时直接通过 socket 发送 RDB
repl-diskless-sync-delay 5     # 延迟 5 秒启动传输,等待更多副本加入
repl-diskless-load disabled    # 副本先将 RDB 落盘,再加载到内存
repl-timeout 60                # 60 秒内无有效复制 I/O 则认为链路超时
client-output-buffer-limit replica 256mb 64mb 60 # 限制慢副本占用的主节点输出缓冲
min-replicas-to-write 1        # 至少有 1 个合格副本时才接受写入
min-replicas-max-lag 10        # 最近 10 秒内有 ACK 的副本才计为合格

下面只展开会直接改变全量同步成本、部分重同步窗口或写入可用性的参数。参数 列表示配置值的格式,具体可选值与默认值应以当前 Redis 版本的 redis.conf 为准。

配置项 参数 作用说明
repl-backlog-size <size> 设置主节点为部分重同步保留的近期复制流容量。容量越大,可覆盖的断线窗口越长,但会占用更多内存。
repl-backlog-ttl <ttl> 设置最后一个副本断开后继续保留 backlog 的秒数;0 表示不因空闲而释放。
repl-diskless-sync <yes|no> 控制全量同步时是否由主节点通过 socket 直接发送 RDB,从而省去中间 RDB 文件的磁盘 I/O。
repl-diskless-sync-delay <seconds> 设置 diskless sync 开始前的等待时间,以便让多个副本共享同一次快照;等待也会推迟首个副本开始同步。
repl-diskless-load <disabled|on-empty-db|swapdb> 控制副本如何加载无盘传输的 RDB,在落盘加载、空库时直接加载和使用临时数据库加载后切换之间选择。
repl-timeout <seconds> 设置复制链路无有效 I/O 时的超时时间;过短可能把网络抖动或事件循环停顿误判为断线。
client-output-buffer-limit replica <hard-limit> <soft-limit> <soft-seconds> 限制主节点为慢副本积压的输出缓冲:触及硬上限,或持续超过软上限达到指定时间时,主节点会断开该副本。
min-replicas-to-write <count> 设置主节点接受写入所需的最少合格副本数;副本不足时拒绝写入,以可用性换取更小的数据丢失窗口。
min-replicas-max-lag <seconds> 设置副本被计为合格副本所允许的最大 ACK 间隔,与 min-replicas-to-write 配合使用。

先用 CONFIG GET 确认实例的运行值,再核对当前版本的 redis.conf。部分参数支持 CONFIG SET,但运行时修改不等于配置已经持久化;重启后的值仍取决于配置文件或托管平台。

4.3 最小验证实验#

下面的端口只是示例,命令只能对可随时丢弃的本地 Redis 实例执行。假设 63796380 上已经各有一个正在运行的实例,分别进入两个端口的 redis-cli。先让 6380 成为 6379 的副本,再查看双方复制状态:

127.0.0.1:6380> REPLICAOF 127.0.0.1 6379
127.0.0.1:6380> INFO replication
127.0.0.1:6379> INFO replication
127.0.0.1:6379> INFO stats

重点查看 master_replidmaster_repl_offset、副本 offset,以及 repl_backlog_activerepl_backlog_first_byte_offsetrepl_backlog_histlenINFO stats 中的 sync_fullsync_partial_oksync_partial_err 可以帮助判断最近走过哪种同步路径。首次建立关系通常能在日志中看到全量同步。

调整 backlog 后,可以主动断开主节点上的复制连接,观察副本自动重连:

127.0.0.1:6379> CONFIG SET repl-backlog-size 16mb
127.0.0.1:6379> CLIENT KILL TYPE REPLICA

CLIENT KILL 只切断连接,不会取消复制配置。副本会自动重连。若 ID 仍可识别且缺失范围尚在 backlog,日志会记录部分重同步;覆盖范围不足时则会出现全量同步。测试不同 backlog 大小后,应同时比较日志、offset、backlog 范围和 INFO stats 计数,不能只看连接最终是否恢复。

WAIT 要在写入所用的同一交互连接中验证:

127.0.0.1:6379> SET replication:demo 1
OK
127.0.0.1:6379> WAIT 1 1000
(integer) 1

这里的整数表示在 1000 毫秒内,有一个副本 ACK 了这条连接此前写入对应的 offset。如果没有健康副本,结果可能是 0;示例中的 (integer) 1 不是任何环境下都必然得到的输出。

4.4 常见问题与排查#

  1. 副本无法建立复制连接

    现象: 副本的 master_link_status 长时间为 down,日志中没有进入全量同步或部分重同步,master_last_io_seconds_ago 也持续增大。

    排查流程:

    1. 在副本执行 INFO replication,确认主节点地址、端口和当前同步状态。
    2. 检查副本到主节点的网络、端口、TLS 配置和防火墙规则。
    3. 对照两端 ACL,核对 masterusermasterauth 以及复制用户所需权限。
    4. 如果握手已经完成但同步中断,再检查主节点日志、全量同步耗时和 repl-timeout
  2. 频繁发生全量同步

    现象: INFO stats 中的 sync_fullsync_partial_err 持续增加,日志反复出现 +FULLRESYNC,同步期间延迟和资源占用明显升高。

    排查流程:

    1. 比较副本请求的 replication ID 与主节点的当前 ID、secondary ID,先确认复制历史能否识别。
    2. 对照请求 offset、repl_backlog_first_byte_offsetrepl_backlog_histlen,确认缺失区间是否仍在 backlog。
    3. 如果 backlog 覆盖不足,根据高峰复制流量和可容忍断线时长重新估算 repl-backlog-size
    4. 如果历史与覆盖范围都正常,继续检查网络中断、进程重启、主节点切换和输出缓冲超限。
  3. 复制延迟持续增大

    现象: 主节点与副本的 offset 差值不断扩大,副本读到旧数据,连接却仍显示为 up

    排查流程:

    1. 连续采样两端 offset,确认差值是短时波动还是持续增长。
    2. 查看 CLIENT LIST 和 Redis 日志,确认副本连接的输出缓冲是否在增长。
    3. 比较主节点写入速率与复制网络吞吐,再检查副本 CPU 是否繁忙、命令执行是否过慢。
    4. 如果正在全量同步,继续观察磁盘吞吐、内存峰值和 RDB 加载耗时。
  4. 副本反复断开并自动恢复

    现象: master_link_statusupdown 之间切换,日志连续出现重连,严重时还会从部分重同步退化为全量同步。

    排查流程:

    1. 用两端日志对齐断开时间,先区分网络重置、超时、进程暂停和主节点主动关闭连接。
    2. 确认 repl-timeout 大于 repl-ping-replica-period,并检查是否存在长时间事件循环阻塞。
    3. 查看副本输出缓冲是否触及 client-output-buffer-limit replica
    4. 找到副本消费不动的原因后再调整超时或缓冲;直接放宽限制可能只会推迟内存耗尽。
  5. 故障转移后出现已确认写入丢失

    现象: 客户端已经收到写入成功响应,但新主节点上找不到这次写入。

    排查流程:

    1. 还原客户端收到响应、原主节点故障和副本晋升的时间顺序。
    2. 确认目标副本在故障前处理到哪个 offset,判断写入是否进入该副本。
    3. 检查业务是否在同一连接执行 WAIT,以及 WAIT 实际返回的副本数。
    4. 核对 min-replicas-to-writemin-replicas-max-lag 和各节点的 RDB/AOF 策略。复制 ACK 不等于数据已经落盘。

Redis 用快照建立数据基线,用复制流持续传递变更;replication ID 识别历史,offset 标记进度,定长 backlog 保存可补发的近期字节。主节点不必保存无限历史,也不用默认等待副本,内存占用和写入延迟因此有了明确边界;代价是全量同步的资源开销,以及异步复制留下的数据窗口。Sentinel 与 Redis Cluster 在此之上完成不同范围的故障转移,但数据丢失上限仍由复制延迟、确认策略和各节点持久化配置共同决定。

参考资料#

访问量 访客数