「Redis 故障转移会丢数据」这句话人人都说,但丢多少、什么时候丢,取决于故障的形态。这篇不复述结论,把三类故障各做一遍,数字并排放在一起:最常见的 kill -9 几乎不丢,而真正会丢的是「旧主库还活着、还有人在写它」的情形。
== 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。这些数字来自本机回环、单一拓扑,请当作「哪类故障风险最大」的相对判断,不是生产环境的预测值。