Redis cluster扩缩容

Redis Cluster 扩缩容

Redis Cluster 通过重新分配哈希槽来扩缩容:扩容时加入空主节点,再把部分槽及槽内数据迁入;缩容时把待删除主节点的槽及数据迁出,清空后再移除节点。

本文以开源 Redis Cluster 为例。主从复制、Sentinel 部署没有这里的槽位分片机制。下面的底层原理采用传统的 MIGRATING / IMPORTING + MIGRATE 流程;新版原子槽迁移见第 7 节。

1. 数据如何分布在节点上

Redis Cluster 固定有 16384 个哈希槽,编号为 0~16383。客户端通过两层映射定位数据:

1
Key → 哈希槽 → 负责该槽的主节点

普通 Key 的槽位计算方式为:

1
slot = CRC16(key) % 16384

Key 含有有效的 Hash Tag 时,Redis 只对 Tag 内容计算 CRC16:找到第一个 { 和它后面的第一个 },两者之间非空时使用中间内容,否则对整个 Key 计算。例如 user:{1001}:profile 和 user:{1001}:orders 属于同一个槽,便于执行同槽多 Key 操作。槽位计算和 Hash Tag 规则见 Redis Cluster 规范。

主节点负责一组槽,副本节点复制其主节点的数据。稳定状态下,每个槽只有一个主节点负责,扩缩容期间槽总数保持 16384。

调整方式 槽位处理 数据处理
新增主节点 从现有主节点迁入部分槽 迁入这些槽中的 Key
删除主节点 把该节点的所有槽迁给其他主节点 迁出这些槽中的 Key
新增副本节点 不改变槽归属 通过主从复制同步数据
删除副本节点 不改变槽归属 不需要重新分片,但会减少副本保护

节点加入集群后,需要运维人员或管理工具发起槽位迁移。 add-node 只完成节点加入,不会给新主节点分配槽。官方扩缩容文档

2. 扩容流程:3 个主节点扩为 4 个

假设现有主节点为 A、B、C,新主节点为 D。按相同权重分配,目标槽数如下:

主节点 扩容前槽数 扩容后目标槽数
A 5461 4096
B 5461 4096
C 5462 4096
D 0 4096
合计 16384 16384

这张表描述目标分配;具体迁哪些槽、各源节点迁多少,由迁移计划决定。节点可以持有多个不连续的槽区间。

2.1 检查集群并准备新节点

以下命令使用示例地址。NODE_A、NODE_B、NODE_C、NODE_D 等字符串需要替换为 CLUSTER NODES 中的真实节点 ID;命令中使用完整 ID。

1
2
redis-cli --cluster check 10.0.0.1:6379
redis-cli -h 10.0.0.1 -p 6379 CLUSTER NODES

新节点需要启用 cluster-enabled yes,使用独立的数据目录和集群配置文件,且没有旧数据或旧集群配置。节点间应能访问客户端端口和集群总线端口;客户端还需要能够连接新节点的公布地址。

若启用了 ACL、密码或 TLS,应补齐对应连接参数,并核对节点间迁移所需的认证配置。下面省略认证参数。

2.2 加入空主节点

1
redis-cli --cluster add-node 10.0.0.4:6379 10.0.0.1:6379

第一个地址是新节点,第二个地址是现有集群的入口节点。redis-cli 检查节点状态并通过 CLUSTER MEET 建立联系,节点随后通过集群通信交换拓扑信息。D 此时没有槽,也不承载分片数据。

2.3 迁入槽位和数据

可以使用交互式重新分片:

1
redis-cli --cluster reshard 10.0.0.1:6379

依次指定迁移槽数 4096、目标节点 D 的 ID、源节点 all,检查计划后确认。all 表示从其他主节点抽取槽,具体分配可能有取整差异。

指定参数的等价示例:

1
2
3
4
redis-cli --cluster reshard 10.0.0.1:6379 \
--cluster-from all \
--cluster-to 'NODE_D' \
--cluster-slots 4096

如果希望管理工具按权重平衡整个集群,可以选择 rebalance。它与上面的 reshard 是两种选择,不需要为了同一次扩容连续执行两遍。

1
2
3
4
5
6
7
8
# 预览计划,不执行迁移
redis-cli --cluster rebalance 10.0.0.1:6379 \
--cluster-use-empty-masters \
--cluster-simulate

# 按计划执行迁移
redis-cli --cluster rebalance 10.0.0.1:6379 \
--cluster-use-empty-masters

--cluster-use-empty-masters 让尚未持有槽的新主节点参与平衡。rebalance 按槽数和权重计算目标分配,不会按 Key 大小或访问热度计算负载。参数定义可参考 Redis CLI 实现。

2.4 给新主节点配置副本并验收

可以在迁移前为 D 添加副本,也可以在迁移后补齐;需要同步检查副本复制状态。

1
2
3
4
5
redis-cli --cluster add-node 10.0.0.5:6379 10.0.0.1:6379 \
--cluster-slave \
--cluster-master-id 'NODE_D'

redis-cli --cluster check 10.0.0.1:6379

--cluster-slave 是命令行保留的参数名,这里的节点角色为 replica。add-node 不会替新主节点创建副本实例。

3. 缩容流程:4 个主节点缩回 3 个

假设 A、B、C、D 各有 4096 个槽,准备删除 D,可以规划如下迁移:

1
2
3
D → A:1365 个槽
D → B:1365 个槽
D → C:1366 个槽

迁移后 A、B、C 分别持有 5461、5461、5462 个槽,D 的槽数为 0。

3.1 把待删除节点的所有槽迁出

先检查剩余节点的内存和吞吐容量,再分批执行:

1
2
3
4
5
6
7
8
redis-cli --cluster reshard 10.0.0.1:6379 \
--cluster-from 'NODE_D' --cluster-to 'NODE_A' --cluster-slots 1365

redis-cli --cluster reshard 10.0.0.1:6379 \
--cluster-from 'NODE_D' --cluster-to 'NODE_B' --cluster-slots 1365

redis-cli --cluster reshard 10.0.0.1:6379 \
--cluster-from 'NODE_D' --cluster-to 'NODE_C' --cluster-slots 1366

每次执行后核对剩余槽数,实际数量按集群当前状态调整。槽内 Key 会随迁移一起转移,不能只删除槽映射。

3.2 处理副本并确认节点为空

如果 D 有副本,可以把副本挂到保留的主节点,也可以根据容量规划移除它。重新指定复制关系的示例:

1
2
# 在 D 的副本上执行,改为复制 A
redis-cli -h 10.0.0.5 -p 6379 CLUSTER REPLICATE 'NODE_A'

更换主节点可能触发全量同步,应检查新复制链路。随后检查 D 的本地状态:

1
2
3
redis-cli -h 10.0.0.4 -p 6379 CLUSTER NODES
redis-cli -h 10.0.0.4 -p 6379 DBSIZE
redis-cli --cluster check 10.0.0.1:6379

确认 D 没有槽、没有残留迁移状态,且 DBSIZE 为 0。若槽已迁完但本地仍有 Key,需要排查残留数据的槽归属。

3.3 移除空节点

1
redis-cli --cluster del-node 10.0.0.1:6379 'NODE_D'

管理工具通知集群忘记 D。确认其他节点的拓扑中已无 D 后,再停止其 Redis 实例,并移除应用中的旧入口地址。删除仍有槽的主节点前,必须先完成重新分片。官方删除节点流程

4. 底层原理:一个槽如何从 A 迁移到 D

以槽 1000 为例,运维工具需要协调数据迁移和槽归属更新。整个槽的传统迁移由多条命令组成,期间槽内 Key 可以分别存在于 A 和 D。

4.1 先准备目标节点,再标记源节点

1
2
3
4
5
# 在目标节点 D 上执行
CLUSTER SETSLOT 1000 IMPORTING NODE_A

# 在源节点 A 上执行
CLUSTER SETSLOT 1000 MIGRATING NODE_D

IMPORTING 表示 D 准备从 A 接收槽内数据;MIGRATING 表示 A 正在把槽迁给 D。先设置 D,才能让它接住 A 随后返回的 ASK 重定向。

此时 A 仍是槽的正式所有者。设置迁移状态不会立刻把整个槽的归属切到 D。

4.2 分批迁移槽内 Key

在 A 上循环执行:

1
2
3
4
5
# 返回槽 1000 中最多 100 个 Key
CLUSTER GETKEYSINSLOT 1000 100

# 下方 key1、key2 要替换为本批返回的真实 Key
MIGRATE 10.0.0.4 6379 "" 0 5000 KEYS key1 key2

MIGRATE 在源节点序列化 Key,在目标节点恢复数据,收到成功响应后从源节点删除。正常迁移会保留 Key 的值和过期语义。空字符串与 KEYS 用于批量迁移,0 是目标数据库,5000 是传输空闲超时的毫秒数;Cluster 只使用数据库 0。MIGRATE 文档

这里不使用 COPY,因为需要清空源节点的数据。MIGRATE 超时可能导致两端都保留 Key,处理错误时应核对两端状态;不能把超时直接理解成“目标端没有写入”。

循环到 CLUSTER GETKEYSINSLOT 1000 100 返回空结果,或 CLUSTER COUNTKEYSINSLOT 1000 为 0,再更新槽归属。

4.3 更新槽位所有权

1
2
3
4
5
6
7
8
# 先在目标节点 D 上执行
CLUSTER SETSLOT 1000 NODE NODE_D

# 再在源节点 A 上执行
CLUSTER SETSLOT 1000 NODE NODE_D

# 随后可在其他主节点上执行同一条命令,尽快更新路由
CLUSTER SETSLOT 1000 NODE NODE_D

D 确认成为槽的新所有者后,A 才放弃归属。相关节点清除迁移状态,并通过集群通信传播配置。若先让 A 放弃槽,而 D 尚未确认归属就发生故障,可能留下没有所有者的槽。

上述顺序和状态转换见 CLUSTER SETSLOT 文档。实际操作优先使用 redis-cli --cluster reshard,由工具协调这些步骤。

5. 在线迁移期间,客户端如何读写

客户端缓存“槽 → 主节点”的路由表。槽 1000 正在从 A 迁往 D 时,客户端可能仍把请求发给 A。

请求情形 源节点 A 的行为 客户端后续处理
单 Key 仍存在于 A 在 A 执行读写 本次请求完成
单 Key 在 A 上不存在 返回 ASK,指向 D 在 D 的同一连接先发 ASKING,再重发该命令
槽归属已切到 D,请求仍发给 A 返回 MOVED,指向 D 更新槽路由并重发命令
同槽多 Key 只部分存在于 A 返回 TRYAGAIN 等待后按客户端策略重试

迁移中的源节点根据 Key 是否存在来处理请求。Key 已迁走和新 Key 在 A 上都不存在,因此都会转向 D;已有 Key 留在 A 时,其更新仍在 A 执行,迁移时再把当前值转到 D。

ASK 只改变本次请求的目的地,客户端保留原槽路由。MOVED 表示槽归属已发生变化,客户端需要修正路由。D 在 IMPORTING 状态下要求客户端先发送 ASKING,避免客户端绕过迁移协议把尚未迁移的 Key 写到 D。

多 Key 命令本来就要求 Key 位于同一槽;传统迁移还可能把同槽 Key 暂时分散到两个节点,因此客户端需要处理 TRYAGAIN。Hash Tag 解决同槽问题,不能消除传统迁移期间的这种情况。SETSLOT 的请求处理规则

在线重新分片允许集群继续服务。迁移仍会消耗 CPU、网络和内存,传统 MIGRATE 还会在传输期间阻塞相关实例;大 Key 迁移尤其容易抬高请求延迟。应按应用的延迟目标调整批量,并监控客户端超时和重试。MIGRATE 的执行行为

6. 分配策略与迁移验收

6.1 槽数均衡与负载均衡

相同容量节点可以先按槽数均分,但槽内 Key 数、数据大小和访问频率可能差异很大。评估迁移效果时,需要一起看槽数、内存占用和请求负载。

不同容量的节点可以设置权重。例如 D 的权重为 2,其他节点为默认的 1,则 D 的目标槽数约占总数的 2 / 5:

1
2
3
4
redis-cli --cluster rebalance 10.0.0.1:6379 \
--cluster-use-empty-masters \
--cluster-weight 'NODE_D=2' \
--cluster-simulate

检查计划后去掉 --cluster-simulate 执行。权重表达容量规划,工具仍按槽数平衡。单个热 Key 或大 Key 无法通过增加节点拆成多个分片,需要应用调整数据模型。

6.2 完成标准

1
2
redis-cli --cluster check 10.0.0.1:6379
redis-cli -h 10.0.0.1 -p 6379 CLUSTER INFO

验收时检查:

  • cluster_state:ok,cluster_slots_assigned:16384,cluster_slots_ok:16384。
  • cluster_slots_pfail:0、cluster_slots_fail:0,全部槽有明确归属,节点对槽配置的认知一致。
  • 传统迁移中没有残留 MIGRATING / IMPORTING 状态。需要在各节点查看 CLUSTER NODES,它只展示本节点的迁移状态。
  • 保留主节点的副本连接正常,复制进度符合预期;应用可以连到新的槽所有者。
  • 请求延迟、内存和节点负载符合扩缩容后的容量规划。

如果迁移中断,先检查源端和目标端的 Key、槽归属及迁移状态,再决定继续迁移还是修复。redis-cli --cluster fix 会修改集群状态,应在确认异常原因和修复方案后使用。

7. 新版原子槽迁移

Redis 8.4 引入 CLUSTER MIGRATION,支持服务端原子槽迁移。在目标主节点上可以发起导入并查询任务状态:

1
2
3
# 以下 Redis 命令在目标主节点上执行
CLUSTER MIGRATION IMPORT 1000 1099
CLUSTER MIGRATION STATUS ALL

该机制与逐 Key 的传统迁移不同,服务端按原子槽迁移协议完成任务。IMPORT 返回任务 ID;需要查询任务完成状态后再验收,而不是收到 ID 就认为迁移结束。CLUSTER MIGRATION 文档

Redis 8.10 的发布说明记录了 redis-cli --cluster reshard 和 rebalance 接入服务端原子槽迁移的变化。实际使用时核对服务端版本和 redis-cli 版本;第 4、5 节描述的是传统机制,不能套用到全部新版迁移实现。Redis 8.10 CLI 变更


Redis cluster扩缩容
http://example.com/2026/10/09/Redis-cluster扩缩容/
作者
Kon4tsu
发布于
2026年10月9日
许可协议