当主重启时 sentinel 没有切换导致数据丢失

说来惭愧,codis 是去年年初就开始调研的。因为想 codis 和 k8s 一起上,重心一直在 k8s 这边。

导致 codis 上线一拖再拖,我们 DBA 用着 twemproxy 那一套老旧的方案快奔溃了。

上个月调研的时候发现 codis 改了 HA 的方案,上了 sentinel。

我们之前在 codis-HA 方案上确实是踩了坑,有一套环境丢了数据。希望 sentinel 能更好一些。但是很快就发现了数据丢失的情况。

复现过程

1 启动一个客户端对 redis 进行写操作,这个客户端全部写完后校验数据有没有缺失

2 强杀 redis,随后立刻重启(sentinel 配置的故障时间内都可以复现)

3 sentinel 检测到重启行为,日志输出 reboot

4 但是数据校验失败

在操作过程中可以明显看到 slave 的 key 在主启动以后减少了几 W 数据

分析

在 sentinelRefreshInstanceInfo 函数中,sentinel 会通过 info 对 master 的 runid 进行检测,当发现 runid 不一致时,输出 reboot 到日志中去

并不进行主从切换,这就导致了主启动时 load 的 RDB 数据较旧,并且会 sync slave 导致 slave 的数据丢失

去 github 看了下 issues,这个问题在 14 年就存在了

Sentinel does not trigger failover in case of master node reboot

emmmm,antirez 的反射弧有点长,最近才关注了这个问题。

他认为 "Only in a very odd condition",导致的这种问题,"For this reason to failover on reboot by default looks a bit too harsh"

他提出的解决方案是

1 添加 PSYNC2 特性,不允许从未知历史记录的复制 ID 进行数据复制,这是防止从数据也丢失了

2 另外或许可以使主一旦重启就进行 failover

从这个解决方案看短期内怕是起码得一个月的功夫。

然后 codis 那里提了 issue 进行合并,redis 从 3.2 到 4.0 估计也得很久(4.0 代码可能有重构)。

所以我的目前解决方案是

取消 k8s 将 codis 进行域名管理的特性,全部使用 ip。重启以后会是一个新的 IP,自然会从主切换成从了。

但是这里会有点小问题

* 以前的主IP需要删除(重启以后k8s会保证IP不重复,但是不能保证下一个启动的容器不使用这个IP)

要删除已经存在sentinel的信息,要对所有sentinel发送SENTINEL RESET 组名,来重置主从情况,

待续

发现 SENTINEL RESET 组名进行删除必须得已经存在新的主,如果 k8s 启动新的容器时执行该指令的时候,新的主没有选举出来,会导致从全部被清除,从而无法切换

所以这一条路走不通。。

新的解决方案是

codis 使用域名进行管理,在 docker 启动容器前执行 SENTINEL failover 组名,来强行切换该组

对于每组 1 主 1 从的架构来说

  • 如果这一组从挂了,会报出 (error) NOGOODSLAVE No suitable slave to promote,不会导致切换

  • 如果主挂了,自然会强行切换,redis 本身的 reboot 不导致切换的特性不会发生

这是一个临时解决方案,毕竟可能会存在多从的场合

这个问题持续更新吧。