Redis 脑裂可以彻底解决吗

170 阅读3分钟

什么是脑裂

在分布式系统中,因为网络分区或节点故障导致节点之间的通信断开,而各自形成一个新的小规模集群,并且各小集群当中,会选举新的master节点,都对外提供独立的服务。这种情况就称为脑裂

简而言之集群中不同节点对于Master的选择出现了分歧,出现了多个Master现象。

为什么会产生脑裂

网络问题: Redis Master 节点与 Sentinel 网络之间存在故障,导致Redis Master节点跟Redis Slave节点和Sentinel集群处于不同的网络分区,此时因为Sentinel集群无法感知到master的存在,所以将Slave节点提升为Master节点。此时存在两个不同的Master节点,就像一个大脑分裂成了两个。

image.png 暂时无法在飞书文档外展示此内容

  • 主机资源问题: redis Master节点所在的服务器上的其他程序临时占用了大量资源(例如 CPU 资源),导致主库资源使用受限,短时间内无法响应心跳,于是Sentinel集群误以为主节点挂了,重新选举了新的Master,当其它程序不再使用资源时,旧Master节点又恢复正常,同一集群下出现两个Master

  • Redis 主节点阻塞: 主库自身遇到了阻塞的情况,例如,处理 bigkey 或是发生内存 swap,短时间内无法响应心跳,还是会触发Sentinel机制,等主库阻塞解除后,又恢复正常的请求处理了。

脑裂产生问题的本质原因是,Redis 主从集群内部没有通过共识算法,来维护多个节点的强一致性。它不像ZK那样,每次写请求必须大多数节点都写成功后才认为成功,当脑裂发生时,ZK主节点被孤立,此时无法写入大多数节点,写请求会直接失败,因此它可以保证集群数据的一致性。

脑裂的后果

数据丢失, 当旧的 Master 变为 Slave 之后,它的执行流程如下:

  1. Slave(旧 Master)会向 Master(新)申请全量数据。
  2. Master 会通过 bgsave 的方式生成当前 RDB 快照,并将 RDB 发送给 Slave。
  3. Slave 拿到 RDB 之后,先进行 flush 清空当前数据(此时第四步旧客户端给他的发送的数据就丢失了)。
  4. 之后再加载 RDB 数据,初始化自己当前的数据。

从以上过程中可以看出,在执行到第三步的时候,原客户端在旧 Master 写入的数据就丢失了,这就是数据丢失的问题。

一致性

  • 在脑裂发生时,集群中可能存在多个Redis 网络分区,如果客户端请求不同的网络分区,得到的数据是不一致的。

如何解决脑裂

Redis 可以配置min-slaves-to-write 和 min-slaves-max-lag,但是当脑裂发生时,它还是无法严格保证数据不丢失,而只能是尽量减少数据的丢失。

在这种情况下,新主库之所以会发生数据丢失,是因为旧主库从阻塞中恢复过来后,收到的写请求还没同步到从库,从库就被哨兵提升为主库了。如果哨兵在提升从库为新主库前,主库即时把数据同步到从库了,那么也不会发生数据丢失,但是这种临界点的情况还是有可能发生。

这就又回到了Redis的定位:Redis 集群是 AP,而不是CP