首页 > 首页 > 数据库 > 非关系型数据库 > Redis > Redis 3.20集群迁移slots槽点丢失key数据
2024
09-27

Redis 3.20集群迁移slots槽点丢失key数据

一、问题

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,并保证在测试过程中不会导致内存满等其他问题

Redis 3.20集群迁移slots槽点丢失key数据 - 第1张  | 架构迷

key格式:{test}i, 保证所有的key使用同一个slot。{test}i的slot为6918,并且测试前slot里面没有key.

Redis 3.20集群迁移slots槽点丢失key数据 - 第2张  | 架构迷

1.非过期key测试

Redis 3.20集群迁移slots槽点丢失key数据 - 第3张  | 架构迷

结论: 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 3.20集群迁移slots槽点丢失key数据 - 第4张  | 架构迷

结论:问题同样复现,不是这里的问题。

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如下:

Redis 3.20集群迁移slots槽点丢失key数据 - 第5张  | 架构迷

全部是4.0时,cluster nodes如下:

Redis 3.20集群迁移slots槽点丢失key数据 - 第6张  | 架构迷

所以继续使用3.2.6的排查。

5.问题在哪?

修改redis-trib代码添加迁移的key 的log,修改redis 3.2.6的代码,在源节点的log里面打印处理的key

Redis 3.20集群迁移slots槽点丢失key数据 - 第7张  | 架构迷

redis 3.2.6

Redis 3.20集群迁移slots槽点丢失key数据 - 第8张  | 架构迷

问题复现,这次丢失的key为:

Redis 3.20集群迁移slots槽点丢失key数据 - 第9张  | 架构迷

在redis-trib和source节点都能看到缺少的key的日志,很明显的看到key被迁移了。

在target的节点看到日志是这样的:

Redis 3.20集群迁移slots槽点丢失key数据 - 第10张  | 架构迷

所有丢失的key的ttl都不对!

去源节点看一下ttl是啥?

Redis 3.20集群迁移slots槽点丢失key数据 - 第11张  | 架构迷

说明所有丢失key的ttl因为没有处理而使用了前一个key的ttl!

问题出在下面代码的for循环,对于不过期的key,ttl应该是0,但是如果前面有过期的key,ttl>0.那么在下一个处理不过期key时,expireat=-1,不会进入if,ttl还是使用前一个ttl,导致一个永不过期的key因为ttl>0而过期。

Redis 3.20集群迁移slots槽点丢失key数据 - 第12张  | 架构迷

三、解决问题

去github看一眼最新的代码,发现已经被@badboy发现并fix了,好悲伤~

官方Git: https://github.com/antirez/redis/pull/3673/files

方法一:需要修改src/cluster.c 源码文件,重新编译:

Redis 3.20集群迁移slots槽点丢失key数据 - 第13张  | 架构迷

方法二:使用redis 4.0 以上版本的redis-trib 迁移slots

作者:字节跳动技术团队
链接:https://www.jianshu.com/p/85cd00588d63

最后编辑:
作者:摘星怪
这个作者貌似有点懒,什么都没有留下。

留下一个回复

你的email不会被公开。