当主重启时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不导致切换的特性不会发生
这是一个临时解决方案,毕竟可能会存在多从的场合
这个问题持续更新吧。