Redis slots迁移丢key问题排查

本文探讨了在Redis 3.2.6版本中,进行slots迁移时可能出现key丢失的现象。通过复现问题,发现如果在迁移过程中有key过期,会导致不过期的key丢失。经过代码分析,发现问题出在过期时间处理上,已修复的PR链接给出。
摘要由CSDN通过智能技术生成

最近在公司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)
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值