掘金 后端 ( ) • 2024-04-01 10:18

前言

Redis集群部署方案都是基于主从架构演变而来的,Redis主从架构是常见的一种数据冗余和热备份技术,对于大型数据应用可以提供非常大的便利,但也有其缺点,比如数据丢失。

由于Redis主从复制默认是异步的,如果主服务器在数据还未来得及复制到从服务器时宕机,可能会发生数据丢失。

本文讨论的主从架构是常见哨兵模式,普通主从模式,主从节点为手动切换,是否丢失数据受人为因素影响。

正文

主从架构下,到底发生了什么?导致的数据丢失。常见的情况有两种

  • 异步复制过程中,master节点宕机;

  • 集群产生脑裂。

异步复制过程中,master节点宕机

Redis 的异步复制(asynchronous replication)过程中,主节点(master)对数据的修改操作会首先在自身完成,然后异步地发送到各从节点(slaves)进行复制。也就是说,主节点上有一部分的修改可能还没有来得及同步到各从节点,而此时如果主节点发生宕机或者其他错误导致服务停止,那么这部分还未来得及复制的数据就会丢失。

详细解释一下,redis 的主从复制基本流程包括:

  • 从节点连接到主节点,发送 SYNC 命令。

  • 主节点在收到 SYNC 命令后执行 BGSAVE 命令,在后台生成 RDB 文件,并使用一个缓冲区记录此后执行的所有写命令(这就是所说的异步复制)。

  • 主节点 BGSAVE 命令执行完毕后,将 RDB 文件发送给从节点,从节点接收并载入这个 RDB 文件,然后开始接收主节点发送过来的缓冲区中的写命令。

在这个过程中,如果主节点宕机,尤其是在步骤3的过程中,那么缓冲区中的写命令可能还没来得及发送给从节点,就会导致数据丢失。这也是为什么主从同步方式是强调数据冗余和服务高可用,并不能保证数据的强一致性。

疑问:master节点开启持久化能解决这个问题吗?

集群产生脑裂

简单来说,集群脑裂是指一个集群中有多个主节点,像一个人有两个大脑,到底听谁的呢?

例如,由于网络原因,master与slave节点之间断开了联系,哨兵检测后认为主节点故障,重新选举从节点为主节点,但主节点可能并没有发生故障。此时客户端依然在旧的主节点上写数据,而新的主节点中没有数据,在发现这个问题之后,旧的主节点会被降为slave,并且开始同步新的master数据,那么之前的写入旧的主节点的数据被刷新掉,大量数据丢失。

image.png

如何解决“脑裂”导致的数据丢失,关键点在于t3时刻阻止客户端往主库写入数据,这样t5时刻主从切换时,主库中并没有新数据。

Redis 已经提供了两个配置项来限制主库的请求处理,分别是 min-slaves-to-write 和 min-slaves-max-lag。

  • min-slaves-to-write:这个配置项设置了主库能进行数据同步的最少从库数量;

  • min-slaves-max-lag:这个配置项设置了主从库间进行数据复制时,从库给主库发送 ACK 消息的最大延迟(以秒为单位)。

有了这两个配置项后,我们就可以轻松地应对脑裂问题了。具体咋做呢?我们可以把 min-slaves-to-write 和 min-slaves-max-lag 这两个配置项搭配起来使用,分别给它们设置一定的阈值,假设为 N 和 T。

这两个配置项组合后的要求是,主库连接的从库中至少有 N 个从库,和主库进行数据复制时的 ACK 消息延迟不能超过 T 秒,否则,主库就不会再接收客户端的请求了。

即使原主库是假故障,它在假故障期间也无法响应哨兵心跳,也不能和从库进行同步,自然也就无法和从库进行 ACK 确认了。这样一来,min-slaves-to-write 和 min-slaves-max-lag 的组合要求就无法得到满足,原主库就会被限制接收客户端请求,客户端也就不能在原主库中写入新数据了。也就达到了“t3时刻阻止客户端往主库写入数据”的目的。

总结

哨兵模式下的主从架构会发生数据丢失,两种情况:

  • 异步复制过程中,master节点宕机;

  • 集群产生脑裂。

可以通过限制主从节点切换期间旧主节点写入新数据,两个参数控制:

  • min-slaves-to-write:这个配置项设置了主库能进行数据同步的最少从库数量;

  • min-slaves-max-lag:这个配置项设置了主从库间进行数据复制时,从库给主库发送 ACK 消息的最大延迟(以秒为单位)。

参考资料

脑裂:一次奇怪的数据丢失[https://time.geekbang.org/column/article/303568]