关于Redis 集群中增减节点时,节点槽重分片和再平衡的文章,转载自
具有自动分区功能的 Redis 集群使用一种很酷的方法来组织和存储数据,其中键存储在一个哈希槽中,键空间被分成 16384 个槽。集群中的每个主节点处理 16384 个哈希槽的一个子集。这将集群的上限设置为最多 16384 个主节点,其中每个 Redis 节点至少可以服务一个插槽,尽管建议的集群大小约为 1000 个节点。
仅 Redis Enterprise 支持自动重新分片。这包括重新分片、分片迁移和设置自动平衡触发器而不影响您的应用程序。如果您运行的是 Redis Community 版本,则需要手动执行维护操作。
通常,如果您不使用哈希标签(一种确保在同一哈希槽中分配多个密钥进行多键操作的方法),则密钥应通过使用 CRC16 的计算均匀分布在所有节点上的所有哈希槽中算法。但是,随着数据的增长和要求越来越高,您可能希望:
- 当一些哈希槽集从现有节点移动到新节点时,将新节点作为空节点添加到集群中。
- 从集群中删除一个节点,以便将分配给该节点的哈希槽移动到其他现有节点。
- 重新平衡集群,以便在节点之间移动一组给定的哈希槽。
- 通过将所有哈希槽从旧节点移动到更强大的新节点来升级到新服务器。
因此,如果我们跳过重新平衡,我们最终可能会得到一个不平衡的集群,或者更糟的是,一个完整的节点和许多空节点。
这篇文章将研究如何在 Redis 集群上执行哈希槽维护操作。
确定哈希槽
考虑我们有一个 3 节点的 Redis 集群(所有主节点),如下图所示:
理想情况下,为了生产设置中的高可用性和冗余目的,至少有一个从站连接到每个主站。但是我们将在这篇博文中跳过它。
首先,连接到Redis Cluster,使用“ --cluster check”命令获取已分配槽位的信息:
(redis1)$ redis-cli --cluster check 192.168.11.131:6379
192.168.11.131:6379 (6ac62aa8...) -> 12651 keys | 5461 slots | 0 slaves.
192.168.11.132:6379 (92385b2e...) -> 12658 keys | 5462 slots | 0 slaves.
192.168.11.133:6379 (9026f2af...) -> 12655 keys | 5461 slots | 0 slaves.
[OK] 37964 keys in 3 masters.
2.32 keys per slot on average.
>>> Performing Cluster Check (using node 192.168.11.133:6379)
M: 6ac62aa8dbb80f982ab1b0fa0623fc54d2bbd77b 192.168.11.131:6379
slots:[0-5460] (5461 slots) master
M: 92385b2ea26a1a27c786cbea34a75f155f87f762 192.168.11.132:6379
slots:[5461-10922] (5462 slots) master
M: 6ac62aa8dbb80f982ab1b0fa0623fc54d2bbd77b 192.168.11.133:6379
slots:[10923-16383] (5461 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
根据以上信息,我们可以总结出我们的 3 节点 Redis 集群的哈希槽分布如下:
Host | 哈希槽# | 插槽总数 |
---|---|---|
redis1, 192.168.11.131 | 0 - 5460 | 5,461 |
redis2, 192.168.11.132 | 5461 - 10922 | 5,462 |
redis3, 192.168.11.133 | 10923 - 16383 | 5,461 |
全部的 | 16,384 |
看起来我们的哈希槽均匀分布在我们的节点之间,每个节点拥有 5461 或 5462 个槽。
哈希槽重新平衡
现在,让我们在图片中再添加 2 个 Redis 主节点:
- redis4, 192.168.11.134
- redis5, 192.168.11.135
添加后,如何分配哈希槽?要均匀分配哈希槽,请将总槽除以 Redis 主节点的数量:
每个主插槽 = 16,384 / 5= 3,276.8(或 3,276 余数 4)
因此,前4个节点应该多持有一个1来解释上述划分中剩余的4个,得出以下结论:
插槽总数,16,384 = (3277 x 4) + 3276
现在,回到分布表,我们在添加两个新的 Redis master 后的插槽分布估计应该如下所示:
主持人 | 旧哈希槽# | 旧总插槽数 | 新的哈希槽# | 新总插槽 |
---|---|---|---|---|
redis1, 192.168.11.131 | 0 - 5460 | 5,461 | 0 - 3276 | 3,277 |
redis2, 192.168.11.132 | 5461 - 10922 | 5,462 | 3277 - 6554 | 3,277 |
redis3, 192.168.11.133 | 10923 - 16383 | 5,461 | 6555 - 9831 | 3,277 |
redis4, 192.168.11.134 | 9832 - 13,108 | 3,277 | ||
redis5, 192.168.11.135 | 13,109 - 16,383 | 3,276 | ||
全部的 | 16,384 | 16,384 |
上述计算和估计显示了我们尝试将横向扩展操作前后的哈希槽分布可视化。幸运的是,我们不必单独执行计算并手动重新平衡每个哈希槽,因为 Redis Cluster 提供了必要的工具和功能来处理这个问题。
让我们把它放到一个实际的现实生活中。以下架构说明了我们将要实现的目标:
首先,我们将第四个和第五个节点添加到 Redis 集群中:
$ redis-cli --cluster add-node 192.168.11.134:6379 192.168.11.131:6379 # add redis4
$ redis-cli --cluster add-node 192.168.11.135:6379 192.168.11.131:6379 # add redis5
查看集群信息时,我们应该注意到 cluster_known_nodes 现在是 5,但 cluster_size 仍然是 3:
(redis1)$ redis-cli cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:5
cluster_size:3
cluster_current_epoch:4
cluster_my_epoch:1
cluster_stats_messages_ping_sent:227
cluster_stats_messages_pong_sent:238
cluster_stats_messages_sent:465
cluster_stats_messages_ping_received:234
cluster_stats_messages_pong_received:227
cluster_stats_messages_meet_received:4
cluster_stats_messages_received:465
现在,检查集群节点以了解插槽分布:
(redis1)$ redis-cli --cluster check 192.168.11.131:6379
192.168.11.131:6379 (6ac62aa8...) -> 12651 keys | 5461 slots | 0 slaves.
192.168.11.132:6379 (92385b2e...) -> 12658 keys | 5462 slots | 0 slaves.
192.168.11.133:6379 (9026f2af...) -> 12655 keys | 5461 slots | 0 slaves.
192.168.11.134:6379 (a987d7a9...) -> 0 keys | 0 slots | 0 slaves.
192.168.11.135:6379 (9644ed11...) -> 0 keys | 0 slots | 0 slaves.
[OK] 37964 keys in 3 masters.
2.32 keys per slot on average.
>>> Performing Cluster Check (using node 192.168.11.133:6379)
M: 6ac62aa8dbb80f982ab1b0fa0623fc54d2bbd77b 192.168.11.131:6379
slots:[0-5460] (5461 slots) master
M: 92385b2ea26a1a27c786cbea34a75f155f87f762 192.168.11.132:6379
slots:[5461-10922] (5462 slots) master
M: 6ac62aa8dbb80f982ab1b0fa0623fc54d2bbd77b 192.168.11.133:6379
slots:[10923-16383] (5461 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
请注意 redis4 (192.168.11.134) 和 redis5 (192.168.11.135) 没有分配给它们的哈希槽。这意味着这两个新节点尚未提供任何数据和工作集群的一部分。我们需要首先在所有 5 个节点上重新平衡插槽。
请注意,默认情况下,重新平衡选项将尝试重新平衡集群中参与 Redis 节点上的插槽(忽略新添加的主节点)。例如,当您如下显式运行命令时,您将获得“无需重新平衡”响应:
(redis1)$ redis-cli --cluster rebalance 192.168.11.135:6379
>>> Performing Cluster Check (using node 127.0.0.1:6379)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
*** No rebalancing needed! All nodes are within the 2.00% threshold.
要强制 Redis 重新平衡新的空主节点(redis4 和 redis5)上的哈希槽,我们必须使用 --cluster-use-empty-masters 标志:
(redis1)$ redis-cli --cluster rebalance 192.168.11.135:6379 --cluster-use-empty-masters
>>> Performing Cluster Check (using node 192.168.11.135:6379)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Rebalancing across 5 nodes. Total weight = 5.00
Moving 2186 slots from 192.168.11.132:6379 to 192.168.11.135:6379
#########################################################################################
Moving 1092 slots from 192.168.11.131:6379 to 192.168.11.135:6379
#########################################################################################
Moving 1093 slots from 192.168.11.131:6379 to 192.168.11.134:6379
#########################################################################################
Moving 2185 slots from 192.168.11.133:6379 to 192.168.11.134:6379
#########################################################################################
此时,散列槽现在在 5 个节点之间平衡。我们可以使用检查选项进一步查看其中一台服务器上的集群插槽:
(redis5)$ redis-cli --cluster check 192.168.11.131:6379
192.168.11.131:6379 (6ac62aa8...) -> 7586 keys | 3276 slots | 0 slaves.
192.168.11.132:6379 (92385b2e...) -> 7617 keys | 3276 slots | 0 slaves.
192.168.11.133:6379 (9026f2af...) -> 7602 keys | 3276 slots | 0 slaves.
192.168.11.134:6379 (a987d7a9...) -> 7580 keys | 3278 slots | 0 slaves.
192.168.11.135:6379 (9644ed11...) -> 7579 keys | 3278 slots | 0 slaves.
[OK] 37964 keys in 5 masters.
2.32 keys per slot on average.
>>> Performing Cluster Check (using node 192.168.11.131:6379)
M: 6ac62aa8dbb80f982ab1b0fa0623fc54d2bbd77b 192.168.11.131:6379
slots:[2185-5460] (3276 slots) master
M: 92385b2ea26a1a27c786cbea34a75f155f87f762 192.168.11.132:6379
slots:[7647-10922] (3276 slots) master
M: 9026f2af5a683123abfdd7494da2c73a61803dd3 192.168.11.133:6379
slots:[13108-16383] (3276 slots) master
M: a987d7a94052c6e9426c96ebcd896c2003923eb4 192.168.11.134:6379
slots:[1092-2184],[10923-13107] (3278 slots) master
M: 9644ed114a59dfb8dc95c5d87d9d4d10ca6c4f1b 192.168.11.135:6379
slots:[0-1091],[5461-7646] (3278 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
从上面的输出中,我们可以看出我们现在总共有 5 个 Redis 节点,每个节点都有大约 3276 个插槽,每个节点总共有 7500 个左右的键。它还报告我们有 0 个从站连接到每个主站(虽然不推荐用于生产)。再往下,它报告每个插槽平均 2.32 个密钥,以及插槽配置的摘要,其中所有节点都同意插槽配置,并且涵盖了所有 16,384 个插槽。
至此,我们的哈希槽平衡练习就完成了。
哈希槽重新分片和重新平衡
现在,假设我们要将图中的 redis1 和 redis2 去掉,只留下 redis3、redis4 和 redis5(回到三主集群拓扑),如下图所示:
我们可以简单地重新分片 redis1 和 redis2,如下所示:
要将所有插槽从 redis1 重新分片(迁移出)到 redis3:
(redis1)$ redis-cli --cluster reshard 192.168.11.131:6379 \
--cluster-from 6ac62aa8dbb80f982ab1b0fa0623fc54d2bbd77b \
--cluster-to 9026f2af5a683123abfdd7494da2c73a61803dd3 \
--cluster-slots 3276 \
--cluster-yes
其中,6ac62为redis1的节点ID,9026f为redis3的节点ID。该–cluster-插槽值是多少插槽,我们要移动,在这种情况下,我们想动他们都redis3。使用 --cluster check 选项检索特定节点上的插槽总数。
要将所有插槽从 redis2 重新分片(迁移出)到 redis4:
(redis1)$ redis-cli --cluster reshard 192.168.11.132:6379 \
--cluster-from 92385b2ea26a1a27c786cbea34a75f155f87f762 \
--cluster-to a987d7a94052c6e9426c96ebcd896c2003923eb4 \
--cluster-slots 3276 \
--cluster-yes
其中,92385是redis2的节点ID,a987d是redis4的节点ID。该–cluster-插槽值是多少插槽,我们要移动,在这种情况下,我们想动他们都redis4。使用 --cluster check 选项检索特定节点上的插槽总数。
现在,当我们检查当前槽分布时,我们可以看到 redis1 和 redis2 应该有 0 个键和槽:
(redis1)$ redis-cli --cluster check 192.168.11.131:6379
192.168.11.131:6379 (6ac62aa8...) -> 0 keys | 0 slots | 0 slaves.
192.168.11.132:6379 (92385b2e...) -> 0 keys | 0 slots | 0 slaves.
192.168.11.133:6379 (9026f2af...) -> 15188 keys | 6552 slots | 0 slaves.
192.168.11.134:6379 (a987d7a9...) -> 15197 keys | 6554 slots | 0 slaves.
192.168.11.135:6379 (9644ed11...) -> 7579 keys | 3278 slots | 0 slaves.
您还应该注意到哈希槽不是均匀分布的,与 redis5 相比,redis3 和 redis4 的哈希槽比例要大得多。因此,最好在 redis3、redis4 和 redis5 之间重新平衡哈希槽:
(redis3)$ redis-cli --cluster rebalance 192.168.11.135:6379
>>> Performing Cluster Check (using node 192.168.11.135:6379)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Rebalancing across 3 nodes. Total weight = 3.00
Moving 1093 slots from 192.168.11.134:6379 to 192.168.11.135:6379
###########################################################################
Moving 1091 slots from 192.168.11.133:6379 to 192.168.11.135:6379
###########################################################################
哈希槽现在在我们想要的 3 个活动 Redis 节点之间均匀平衡:
(redis3)$ redis-cli --cluster check 192.168.11.131:6379
192.168.11.131:6379 (6ac62aa8...) -> 0 keys | 0 slots | 0 slaves.
192.168.11.132:6379 (92385b2e...) -> 0 keys | 0 slots | 0 slaves.
192.168.11.133:6379 (9026f2af...) -> 12651 keys | 5461 slots | 0 slaves.
192.168.11.134:6379 (a987d7a9...) -> 12658 keys | 5461 slots | 0 slaves.
192.168.11.135:6379 (9644ed11...) -> 12655 keys | 5462 slots | 0 slaves.
我们现在可以从 Redis 集群中删除 redis1 (192.168.11.131) 和 redis2 (192.168.11.132) 因为它不再有插槽了:
从集群中删除 redis1:
(redis3)$ redis-cli --cluster del-node 192.168.11.131:6379 6ac62aa8dbb80f982ab1b0fa0623fc54d2bbd77b
>>> Removing node 6ac62aa8dbb80f982ab1b0fa0623fc54d2bbd77b from cluster 192.168.11.131:6379
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.
从集群中删除 redis2:
(redis3)$ redis-cli --cluster del-node 192.168.11.132:6379 92385b2ea26a1a27c786cbea34a75f155f87f762
>>> Removing node 92385b2ea26a1a27c786cbea34a75f155f87f762 from cluster 192.168.11.132:6379
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.
验证我们最终的 Redis 节点拓扑和哈希槽的分布:
(redis3)$ redis-cli --cluster check 192.168.11.133:6379
192.168.11.133:6379 (9026f2af...) -> 12651 keys | 5461 slots | 0 slaves.
192.168.11.135:6379 (9644ed11...) -> 12655 keys | 5462 slots | 0 slaves.
192.168.11.134:6379 (a987d7a9...) -> 12658 keys | 5461 slots | 0 slaves.
此时,我们可以安全地停用已删除的节点 - redis1 和 redis2。以上所有操作都可以在不中断Redis服务的情况下实时执行。但是,应用程序必须使用Redis支持的Redis 客户端/驱动程序/连接器,以便在重新平衡或重新分片发生时将请求重定向到正确的分片。
最后的想法
Redis Cluster 具有开箱即用的实时集群重新分片和重新平衡功能。这些操作可以在线执行,而不会中断整个 Redis 服务,从而允许集群对应用程序透明地扩展。