redis - Redis 集群实时重新分片失败
问题描述
我们在生产环境中广泛使用 redis-cluster。我们目前有一个 30 个节点的集群(15 个主节点,15 个从节点)我们正在尝试增加集群,因为我们已经创建了新的服务器并将它们加入到集群中。到目前为止一切都很好。
接下来 - 我们正在尝试将插槽重新分配给新的主人。我们使用命令编写了一个执行此操作的脚本redis-trib
reshard
。
但是 - 迁移中途失败(但距离开始不远)并出现以下错误:
[ERR] Calling MIGRATE: ERR Target instance replied with error: BUSYKEY Target key name already exists.
这种情况偶尔会发生,有时它会在失败之前设法移动一些插槽,有时它会在第一个插槽上失败。每个此类故障都需要手动修复操作,这使得重新分片操作非常难以管理。
除了停机迁移之外,我们还没有找到任何具体的例子,也没有任何关于如何防止这种情况的想法。我们正在努力避免。
版本:
redis server 4.0.2
redis trib 3.3.3(在此问题后从 4.0.2 降级:redis cluster reshard [ERR] Calling MIGRATE: ERR Syntax error)
我们的下一步是升级到最新的 redis (4.0.11),尽管我们在此问题的发行说明中没有发现任何迹象。
希望听到我们做错了什么以及如何修复它,或者 redis-cluster 不是为实时重新分片而构建的?
谢谢
解决方案
在为我们自己的项目使用 redis-clustering 支持时,我遇到了这样的问题。我发现redis-trib reshard
命令有问题。如果没有密钥存储在从一个主节点迁移到另一个主节点的插槽中,则它可以正常工作。
但是redis-5(仍在开发中,还不稳定)有它自己的“redis-cli”,我认为重新分片命令没有问题。仅对于 5 的较低版本才会发生。
如果你查看 redis 的官方文档说redis reconfiguration和redis cluster resharding,你会发现他们在内部对 reshard 做了什么。
所以我通过运行 bash 脚本而不是运行redis-trib reshard
命令来完成这些任务,从而解决了这个问题。
假设您想将一些插槽从主节点重新分片到其他主节点。我们将拥有哈希槽当前所有权的节点称为源节点,我们将要迁移到的节点称为目标节点。
对于每个插槽,请执行以下步骤:
请记住,根据 redis 官方文档,这些步骤的顺序在这里很重要。
- 发送
CLUSTER SETSLOT <slot> IMPORTING <source-node-id>
到目标节点以将槽设置为导入状态。 - 发送
CLUSTER SETSLOT <slot> MIGRATING <destination-node-id>
到源节点以将槽设置为迁移状态。 使用CLUSTER GETKEYSINSLOT命令从源节点获取密钥,并使用以下MIGRATE命令将它们移动到目标节点。
MIGRATE target_host target_port key target_database_id timeout
在 Redis Cluster 中不需要指定除 0 以外的数据库,但MIGRATE是一个通用命令,可以用于不涉及 Redis Cluster 的其他任务。
- 当迁移过程最终完成时,
CLUSTER SETSLOT <slot> NODE <destination-node-id>
在源节点和目标节点中都使用,以便再次将插槽设置为正常状态。通常将相同的命令发送到所有其他节点,以避免等待新配置在集群中自然传播。
此处还提供了一个简单的示例 bash 脚本来执行此操作:
源IP 172.17.0.5
:。源 ID:1f70a5107e0042a7d33a9efaf88dbdfecd78076a
目标IP 172.17.0.4
:。目的地ID:7e428bae84697a3882ecad19bd0d13ac7ee97d02
另一个主IP:172.17.0.7
for i in `seq 0 5460`; do
redis-cli -c -h 172.17.0.4 cluster setslot ${i} importing 1f70a5107e0042a7d33a9efaf88dbdfecd78076a
redis-cli -c -h 172.17.0.5 cluster setslot ${i} migrating 7e428bae84697a3882ecad19bd0d13ac7ee97d02
while true; do
key=`redis-cli -c -h 172.17.0.5 cluster getkeysinslot ${i} 1`
if [ "" = "$key" ]; then
echo "there are no key in this slot ${i}"
break
fi
redis-cli -h 172.17.0.5 migrate 172.17.0.4 6379 ${key} 0 5000
done
redis-cli -c -h 172.17.0.5 cluster setslot ${i} node 7e428bae84697a3882ecad19bd0d13ac7ee97d02
redis-cli -c -h 172.17.0.4 cluster setslot ${i} node 7e428bae84697a3882ecad19bd0d13ac7ee97d02
redis-cli -c -h 172.17.0.7 cluster setslot ${i} node 7e428bae84697a3882ecad19bd0d13ac7ee97d02
done
推荐阅读
- javascript - JS:单击数组中的对象 --> 显示更多信息
- java - 长时间运行的春季计划任务
- java - 从一个列表填充多个列表
- aws-lambda - 在 aws lambda 中使用 LWA 链接我的技能时,如何从我的数据库中获取设备列表?
- docker - 从什么时候将用户添加到 docker 组不足以执行 docker 客户端并且我每次都需要`newgrp docker`?
- c# - 如何验证 ODataQueryOptions
- c# - 如何在soap web services c#中请求并获取响应
- asp.net - 默认网站 IIS asp.net 下的多个应用程序
- javascript - 从四舍五入遇到 toFixed 问题
- java - 将列表项排序为顺序列表