bigywater.tech
Redis · 93

Redis 故障转移中的写入丢失窗口

「Redis 故障转移会丢数据」这句话人人都说,但丢多少、什么时候丢,取决于故障的形态。这篇不复述结论,把三类故障各做一遍,数字并排放在一起:最常见的 kill -9 几乎不丢,而真正会丢的是「旧主库还活着、还有人在写它」的情形。

kill -9:0 条
8 次实验、共 3345077 条已确认写入,新主库上无一丢失
脑裂:1226 / 1540
旧主库在分区中继续被写、恢复后被降级时,已确认写入的丢失
63 条
同样的脑裂加 min-replicas-to-write 1 / max-lag 3 之后的丢失

场景一:kill -9 主库

一主两从 + 3 个 sentinel(down-after 1000 ms);一个线程持续写主库并记录已确认的写入;kill 之后到新主库上逐条检查
实测 · 本机执行输出
== kill -9 主库时,正在被持续写入:已回复 OK 的写入在新主库上还有多少?(每种写法 4 次)
写法                            次      已确认     丢失      丢失占比  丢失的是最后几条?
同步 SET(一条一条等回复)           1    47456      0    0.000%  无丢失
同步 SET(一条一条等回复)           2    47506      0    0.000%  无丢失
同步 SET(一条一条等回复)           3    46673      0    0.000%  无丢失
同步 SET(一条一条等回复)           4    45742      0    0.000%  无丢失
管道 100 条一批                1   790000      0    0.000%  无丢失
管道 100 条一批                2   787900      0    0.000%  无丢失
管道 100 条一批                3   788600      0    0.000%  无丢失
管道 100 条一批                4   791200      0    0.000%  无丢失

同步逐条写(约每秒 2 万多条)与管道 100 条一批(每秒约 40 万条)各做 4 次:3345077 条已确认的写入,在新主库上没有丢失一条。原因我的解释是:进程被杀时,它已经写进套接字的数据还在内核里,会继续被送到从库(这是解释,没有单独验证);本机回环上从库也几乎没有复制延迟。真实网络里复制延迟大、或主机整体掉电时,这个结论不一定成立——本文没有覆盖。

场景二、三:旧主库还活着

会丢数据的是另一类:旧主库并没有消失。冻结后解冻(第 77 篇,集群里),解冻瞬间旧主库仍接受了一次写入并回复 OK,随后被丢弃(3 次里 3 次)。sentinel 与主库分区、客户端仍连着旧主库(第 91 篇):1540 次写入被回复 OK,最终丢失 1226 条(80%),窗口是「旧主库仍接受写入」的整个时长。给旧主库加 min-replicas-to-write 1、max-lag 3(第 94、95 篇)后同一实验丢失 63 条。

结论

丢失窗口的大小,不取决于「有没有故障转移」,而取决于旧主库在被替换之后还能接受多久写入。进程被杀:窗口约等于零;进程被冻住又醒来:毫秒级;网络分区、客户端仍连旧主库:等于整个分区时长;min-replicas-to-write 把窗口压到 max-lag。这些数字来自本机回环、单一拓扑,请当作「哪类故障风险最大」的相对判断,不是生产环境的预测值。

分步演示:三类故障的丢失对比

故障转移中的写入丢失窗口
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Redis 8.10.2 独立实例(本机回环端口,save 关闭),redis-cli 风格输出由自写的 RESP 客户端打印;计时用 time.perf_counter,数字每次运行会有小幅抖动。场景一新做 8 次;场景二引用第 77 篇,场景三引用第 91、95 篇的数据
    • 没有验证:物理机断电、复制延迟很大时的 kill -9、AOF 设置对场景一的影响、Redis Cluster 下的 kill -9 丢失量(第 75 篇有 3 次 0 丢失的小样本)、客户端重试对「已确认」语义的影响
    京ICP备2026067502号-1京公网安备11010502063487号