群发资讯网

我在面试中被问懵了:Redis 集群竟然真的会丢数据?

大家好呀,我是 31 岁的小米,一个喜欢折腾后端技术、也喜欢在深夜写代码到掐点吃烤冷面的程序员。这篇文章我准备讲一个发



大家好呀,我是 31 岁的小米,一个喜欢折腾后端技术、也喜欢在深夜写代码到掐点吃烤冷面的程序员。这篇文章我准备讲一个发生在我身上的真实故事。

如果你正准备跳槽、面 Java、用到 Redis,那你最好拿杯奶茶坐稳。因为这篇文章会把你从“Redis 面试背八股文的苦哈哈选手”,升级成“能把面试官聊到点头称赞的爽文主角”。

而主角的第一关,就是这个经典又狠的面试题:

Redis 集群会有写操作丢失吗?为什么?

别急,我们慢慢来。

故事从一次“自信满满”的面试开始

事情发生在我换工作的那一年。当时我已经有五六年的 Java 经验,自认为 Redis 玩得挺溜,什么五大数据结构、跳表、AOF、RDB、哨兵、集群,这不都懂嘛。

面试那天,我坐在工位对面的玻璃房里,喝着面试官递来的保温杯热水,心想:今天这场,应该稳了。

然而,面试官抬头,看了我一眼,直接来了一句:

“你说说,Redis 集群会有写丢失吗?”

我心里咯噔一下。这问题……不是能不能的问题,而是面试官到底想我从哪儿回答?

因为 Redis 集群的写丢失,取决于:

你说的数据复制?

你说的故障恢复?

你说的 client 端重试?

还是你说的槽迁移?

我灵魂深处那只小猫猫瞬间炸毛。但面试官没给我继续纠结的机会,又补了一句:“从节点切换和网络复制机制两个角度,说说。”

他又补:“如果你觉得不同场景结果不一样,你也可以都讲讲。”

没办法,只能硬着头皮上。于是我告诉他:Redis 集群确实会有写丢失的情况,而且不止一种。

面试官点点头,让我继续说。后来我才知道:

如果我那天回答得再浅一点,估计这面就凉了。

Redis:你以为它不丢,其实它悄悄丢

要回答这个问题,我们得先把 Redis 集群的关键机制捋清,Redis 集群的核心逻辑:

主节点负责写

从节点异步复制

主节点挂了,从节点可能提升为主节点

节点间通过 Gossip 协议感知状态

关键在于这条:Redis 的复制是“异步复制”。

异步就意味着:

主节点写成功了

但还没把写同步到从节点

主节点就挂了

从节点被提升为主节点

此时?数据丢了。

而且 Redis 官方文档一直坦诚地写着:

Redis Cluster 在故障切换期间可能会发生数据丢失。

我第一次看到时甚至愣住三秒:“你居然……承认了???”

但后来我越研究越觉得:

异步复制天生就存在风险,不丢也才奇怪。

下面我们进入正题,把所有“会丢”的场景讲个透。这也是面试最容易加分的地方。

场景一:主从异步复制导致写丢失(最常见,也是必考点)

这是最基础的丢失场景。只要你说出“异步复制”,面试官就知道你至少没从入门群里来。让我们看一下实际发生了什么。

场景步骤:

客户端向 Master 写入 key:set user:1 xm

Master 接收成功,返回 OK

Master 正要把数据复制到 Slave

命运发动——Master 宕机

Redis 集群投票,让 Slave 升级为 Master

新的 Master 中没有这条数据(因为复制没来得及同步)

结果?write lost. 就这么丢了。我们称这种情况为:Replica Lag(复制延迟引发的数据丢失)

这类丢失属于淘宝级别常见,甚至你重试也救不回来。面试官一般希望你讲到:

Redis 的复制无“强一致保障”

不会等待从节点确认

Redis 集群不是 CP,而是 AP(偏可用性)

如果你再补一句:

Redis 这么做是为了保证高吞吐和低延迟,所以牺牲了数据一致性。

面试官会非常满意。

场景二:网络分区导致写丢失(脑裂)

这个场景更有“故事性”。所谓脑裂,就是:

主节点和部分集群通信正常,但在另一部分节点看来,它已经“死了”。

结果:

一部分节点认为 Master 活着

另一部分节点认为 Master 挂了,于是把 Slave 晋升为 Master

此时会产生两个 Master,并接受写入。当网络恢复后,只能保留一个 Master。另一个 Master 上的所有写,全丢。

你可以把这个场景理解为:

Redis 集群在脑裂恢复后,会丢掉被隔离分区上产生的所有写。

为什么?

因为 Redis 的故障恢复依赖多节点投票,当你处于小分区(被少数节点看到),你的写,在大多数节点看来是“无效写”。

这就叫:大多数原则导致的小分区写丢失。

面试官非常喜欢这个点。

场景三:槽迁移时写丢(slot migration)

这个很容易被忽略。当 Redis 集群进行水平扩容或缩容时,会迁移槽(slot)。迁移过程中:

旧节点仍然处理写入

新节点也可能接收到写请求

数据在迁移前后短暂出现不一致

迁移过程中如果节点宕机,中间状态的 key 可能丢失

实际开发中,如果不是专业团队,很可能不小心就把数据弄丢了。

Redis 官方也建议:

槽迁移时,不要有写操作,或做好强制重试机制

否则你真的可能在生产看到:“诶?我的 key 呢?昨天还在的啊!”

场景四:客户端重试机制导致“伪写丢失”

这个场景可能不是真丢,但行为上“看起来像丢”。例如:

客户端发起写请求

网络波动导致客户端以为写失败

实际上服务端已经写成功

客户端重试写另一个节点

数据出现覆盖或冲突

表面上看丢了,其实是客户端逻辑出了问题。但面试官也会认为你把业务层面考虑得很细。

这通常属于加分项。

总结:Redis 集群会不会丢写?——会,而且不止一种方式

面试时最完美的回答结构如下:

Redis 集群确实会出现写丢失,主要原因包含四类:

异步复制引发的数据丢失(最核心原因):Master 写成功后未及时复制到 Slave,主节点宕机导致写丢失。

网络分区导致的脑裂:多个 Master 同时接受写,恢复后弱分区数据被回滚。

槽迁移过程产生的不一致:slot migration 中出现异常导致部分 key 丢失。

客户端重试造成的“伪写丢失”:写成功但客户端误以为失败,导致覆盖或逻辑错误。

然后补一句更高级的:

Redis Cluster 属于 AP 系统,它优先保证可用性,所以一致性不是完全保证的。

最后总结一句灵魂式的:

只要复制不是强同步,就一定存在写丢失风险。

面试官听到这里基本会心一笑。你就知道,这题稳了。

那为什么 Redis 不做“强一致”?

你可以在面试中额外补一句(超加分):

因为强一致复制会带来巨大的性能开销。

这是必然的。想象一下:

Master 每写入一次

都要等待多个 Slave 回复 ack

每次写都阻塞

集群吞吐量会雪崩

延迟至少成倍增加

Redis 的哲学是:

99% 的人更需要高性能,而不是强一致。

如果你真的要强一致,可以使用:

KeyDB(支持多主同步)

Redis 事务(不解决复制一致性)

Raft 协议实现版 Redis(社区版)

但生产场景里,只要你的写请求不是银行级别强一致要求,Redis 的 AP 设计已经够用。

我最后问了面试官一句:你们线上丢过吗?

他笑着点头:

“当然丢过,但系统做了补偿。能承受就没问题。”

那一刻我明白了:

不是所有系统都需要绝对不丢,关键在于业务能不能兜得住。

END

如果你能讲清楚:

redis 为什么会丢?

哪些场景会丢?

为什么官方选择异步?

业务如何容忍?

恭喜你,你已经从“背八股文的应试模式”,进阶到了“能和面试官深入对话的工程师模式”。