最近在公司wiki写了几篇还不错的wiki,感觉有分享的必要,所以贴出来和大家分享。
这个是redis的一个critical级别的bug,在目前的3.2.6的小版本都没有修复,只有在4.0rc2里面修了。就是说即使你们用12月发布的3.2.6都是有问题的。
1.问题
redis slots迁移的时候,在迁移之后key数量会变少.
2.排查
2.1思考
- redis 3.x也是比较成熟的产品了,为什么会丢key?别人有没有遇到同样的问题?
- 假设丢key了,如果key是因为expire丢失,那应该是正常,如果没有expire丢失,就是问题了,首先复现问题。
2.2复现问题
0.准备集群
造了两个节点的集群:10.0.0.10:20003和10.0.0.10:20004,最大可使用内存200M,并保证在测试过程中不会导致内存满等其他问题
10.0.0.10:20004> cluster nodes
2aed426536067077179a3d23875b93b223802dea 10.0.0.10:20003 master - 0 1482132098352 6 connected
03a53320815d8b5f774810f2d41329007d60ebf4 10.0.0.10:20004 myself,master - 0 0 7 connected 0-16383
key格式:{test}_i, 保证所有的key使用同一个slot。{test}_i的slot为6918,并且测试前slot里面没有key.
10.0.0.10:20004> cluster keyslot {test}_i
(integer) 6918
10.0.0.10:20004> cluster countkeysinslot 6918
(integer) 0
10.0.0.10:20003> cluster countkeysinslot 6918
(integer) 0
# 迁移函数,配合redis-trib fix迁移
def migrate_from_4_to_3(slot):
cmd="cluster setslot %s migrating 2aed426536067077179a3d23875b93b223802dea" % (slot,)
cli4.execute_command(cmd)
cmd="cluster setslot %s importing 03a53320815d8b5f774810f2d41329007d60ebf4" % (slot,)
cli3.execute_command(cmd)
def migrate_from_3_to_4(slot):
cmd="cluster setslot %s migrating 03a53320815d8b5f774810f2d41329007d60ebf4" % (slot,)
cli3.execute_command(cmd)
cmd="cluster setslot %s importing 2aed426536067077179a3d23875b93b223802dea" % (slot,)
cli4.execute_command(cmd)
1.非过期key测试
10.0.0.10:20003> cluster countkeysinslot 6918
(integer) 0
10.0.0.10:20004> cluster countkeysinslot 6918
(integer) 0
# 向10.0.0.10:20004写入20000个不带过期时间的key
for i in range(20000):
cmd="set {test}_%s %s" % (i, i)
print cmd, cli4.execute_command(cmd)
10.0.0.10:20004> cluster countkeysinslot 6918
(integer) 20000
# 迁移slot 6918
migrate_from_4_to_3(6918)
redis-trib fix 10.0.0.10:20004
#check
10.0.0.10:20003> cluster countkeysinslot 6918
(integer) 20000
10.0.0.10:20004> cluster countkeysinslot 6918
(integer) 0
结论: 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次在退出试一下。
# 修改后的redis-trib.rb
...
# Migrate all the keys from source to target using the MIGRATE command
zerocounttime = 0
while true
keys = source.r.cluster("getkeysinslot",slot,o[:pipeline])
cntkeysinslot = source.r.cluster("countkeysinslot",slot)