一、问题
redis slots迁移的时候,在迁移之后key数量会变少.
二、排查
2.1、思考
- redis 3.x也是比较成熟的产品了,为什么会丢key?别人有没有遇到同样的问题?
- 假设丢key了,如果key是因为expire丢失,那应该是正常,如果没有expire丢失,就是问题了,首先复现问题。
2.2、复现问题
0.准备集群
造了两个节点的集群:10.1.100.100:20003和10.1.100.100:20004,最大可使用内存200M,并保证在测试过程中不会导致内存满等其他问题
key格式:{test}i, 保证所有的key使用同一个slot。{test}i的slot为6918,并且测试前slot里面没有key.
1.非过期key测试
结论: 20000个key全部迁移,没有问题。
2.部分带过期时间的key测试
使用和上面相同的方法,测试{20000个不过期的key,20000个带过期时间的key}的情况。使用了{test}i的slot=6918和{bug}i的slot=7910这两个slot进行了测试。
如果迁移的过程中没有key正在过期,发现迁移后key的数量也会减少
如果有迁移的过程中有key正在过期,那么迁移完成后key的数量少于20000,并且多次实验测试少的key的数量不同,有的时候少几百个,有的时候少2000多个。
说明,如果在迁移slot的过程中,如果有key过期,那么会对那么没有过期时间的key造成影响,导致丢失一些不过期的key.
3.是不是redis-trib的问题?
redis-trib在判断是不是迁移完成时,只判断了getkeysinslot,当getkeysinslot返回空时就直接认为迁移完成了,直接退出。
所以在代码里面添加了countkeysinslot,当两者同时为0时,在尝试判断10次在退出试一下。
结论:问题同样复现,不是这里的问题。
4.redis新版本如何?
我们用的redis版本为3.2.1,新版本是不是也存在这个问题?
在github上clone了最新的代码
3.x的最新版本3.2.6, 问题可以复现。
4.0, 问题没有复现。并且在测试中发现,4.0的redis-server和当前3.2的无法兼容。
并且在测试中发现,4.0的redis-server和当前3.2的无法兼容。
例如10.1.100.100:20003是3.2.1,10.1.100.100:20004是4.0,cluster nodes如下:
全部是4.0时,cluster nodes如下:
所以继续使用3.2.6的排查。
5.问题在哪?
修改redis-trib代码添加迁移的key 的log,修改redis 3.2.6的代码,在源节点的log里面打印处理的key
redis 3.2.6
问题复现,这次丢失的key为:
在redis-trib和source节点都能看到缺少的key的日志,很明显的看到key被迁移了。
在target的节点看到日志是这样的:
所有丢失的key的ttl都不对!
去源节点看一下ttl是啥?
说明所有丢失key的ttl因为没有处理而使用了前一个key的ttl!
问题出在下面代码的for循环,对于不过期的key,ttl应该是0,但是如果前面有过期的key,ttl>0.那么在下一个处理不过期key时,expireat=-1,不会进入if,ttl还是使用前一个ttl,导致一个永不过期的key因为ttl>0而过期。
三、解决问题
去github看一眼最新的代码,发现已经被@badboy发现并fix了,好悲伤~
官方Git: https://github.com/antirez/redis/pull/3673/files
方法一:需要修改src/cluster.c 源码文件,重新编译:
方法二:使用redis 4.0 以上版本的redis-trib 迁移slots
作者:字节跳动技术团队
链接:https://www.jianshu.com/p/85cd00588d63
- 本文固定链接: http://www.jiagou.cc/1018/
- 转载请注明: 摘星怪 于 架构迷 发表