当主重启时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不导致切换的特性不会发生

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

这个问题持续更新吧。