4. 集群扩容工程
当业务写入量增长、单节点内存接近上限,或希望把热点槽位分散到更多节点时,就需要对 Redis Cluster 做横向扩容。扩容的核心动作可以概括为三件事:把新节点加入集群、把一部分槽位从旧节点重新分配给新节点、把槽位对应的键值数据迁移过去。三者顺序不能颠倒——节点没加入集群就无法接收槽位,槽位没分配就不会触发数据迁移,数据没迁完就不能把槽位标记为稳定归属。
本节以“3 主 3 从扩容为 4 主 4 从”为例,假设现有主节点为 M1、M2、M3,各自负责 5461、5461、5462 个槽位(16384 个槽位按 3 份近似均分),新增主节点 M4 与从节点 S4。目标是把 16384 个槽位重新均分为 4 份,每主约 4096 个槽位,即从 M1、M2、M3 各迁出约 1365 个槽位给 M4。示例中的节点 ID、IP、端口均为教学假设值,实际执行时以 cluster nodes 输出为准。
一、加入节点:先空节点入群,再指定角色
新节点启动时若配置了 cluster-enabled yes,它会以“空集群”状态运行,此时不能直接接收槽位。需要先用 CLUSTER MEET 把它拉进现有集群:
# 在任意已知节点上执行,把新节点介绍给集群
redis-cli -h 10.0.0.11 -p 7000 cluster meet 10.0.0.14 7000
redis-cli -h 10.0.0.11 -p 7000 cluster meet 10.0.0.14 7001
# 确认新节点已出现在集群视图中
redis-cli -h 10.0.0.11 -p 7000 cluster nodes | grep 10.0.0.14
刚加入的节点默认是 master 角色且不持有任何槽位。如果它要作为从节点,需要用 CLUSTER REPLICATE <master-id> 指定主节点;如果作为新主节点 M4,则保持 master 角色,等待后续分配槽位。这里有一个容易忽略的点:CLUSTER MEET 只是让节点互相握手、交换集群配置,并不等于它已经参与槽位分配。此时用 cluster info 查看,cluster_state 仍可能是 ok,但 cluster_known_nodes 会增加,cluster_slots_assigned 不变。
二、分配槽位:用 reshard 把槽位“搬”给新主
Redis 官方提供了 redis-cli --cluster reshard 工具,它把“选源节点、选目标节点、迁移键、更新槽位归属”封装成一次交互。执行时关键输入有四个:目标节点 ID、迁移槽位数量、源节点列表、是否使用 --cluster-yes 自动确认。示例命令如下:
redis-cli --cluster reshard 10.0.0.11:7000 \
--cluster-from <M1-id>,<M2-id>,<M3-id> \
--cluster-to <M4-id> \
--cluster-slots 4096 \
--cluster-yes
这条命令的含义是:从 M1、M2、M3 三个源节点中合计取出 4096 个槽位,全部归到 M4。工具会按源节点当前槽位数量做近似均摊,通常每个源节点贡献约 1365 个槽位。执行过程中会看到类似输出:
Moving slot 0 from 10.0.0.11:7000 to 10.0.0.14:7000
Moving slot 1 from 10.0.0.11:7000 to 10.0.0.14:7000
...
Moving slot 5460 from 10.0.0.11:7000 to 10.0.0.14:7000
槽位迁移不是瞬间完成的。对每个槽位,Redis 采用“先标记 MIGRATING/IMPORTING,再逐键 MIGRATE,最后广播槽位归属”的流程。迁移期间,如果客户端访问的键已经迁走,源节点会返回 ASK 重定向,客户端需要先向目标节点发送 ASKING 再执行命令。这也是为什么扩容最好在业务低峰期做:迁移中的槽位会带来额外的重定向和延迟抖动。
三、迁移数据:观察指标与判断完成
迁移是否完成,不能只看命令是否返回。建议在扩容过程中持续观察以下指标:
cluster_slots_assigned:应始终为 16384,若下降说明有槽位处于异常状态。cluster_slots_pfail/cluster_slots_fail:应为 0,出现非 0 说明有槽位不可达。cluster_known_nodes:应从 6 变为 8,确认新节点已被全集群感知。- 各节点
cluster_countkeysinslot与dbsize:迁移后 M4 的键数量应逐步上升,M1/M2/M3 相应下降。
当 redis-cli --cluster check 10.0.0.11:7000 输出中每个主节点的槽位数量接近 4096、且没有 [ERR] 或 [WARN] 级别的槽位覆盖/空洞提示时,可以认为再平衡完成。此时再用 cluster nodes 确认 M4 的 slots 字段已包含一段连续或分散的槽位区间,例如 0-1364 5461-6825 10923-12287 这类形式。
CLUSTER COUNTKEYSINSLOT 抽样,确认槽位内确实有键;同时用业务侧压测或抽样读写验证 ASK 重定向已消失。若仍有大量 ASK,说明迁移未彻底结束或客户端缓存了旧槽位映射。四、注意事项与常见坑
第一,不要手动用 CLUSTER ADDSLOTS 直接给新节点分配槽位。该命令只改槽位归属,不会迁移已有键,会导致槽位内数据“凭空消失”或访问报 MOVED 到空节点。正确做法始终是 --cluster reshard 或 CLUSTER SETSLOT 配合 MIGRATE。
第二,迁移期间避免对同一槽位做大批量写入。MIGRATE 是同步阻塞的,单键过大(如超过 10MB 的 Hash)会拉长迁移时间,甚至触发 cluster-node-timeout 误判。可先用 --cluster-pipeline 控制批量,或对大键做拆分。
第三,从节点要同步扩容。新增 M4 后,如果不同时给 M4 配一个从节点 S4,M4 故障时其 4096 个槽位将不可用,集群进入 fail 状态。S4 的加入方式与 M4 类似,先 CLUSTER MEET,再 CLUSTER REPLICATE <M4-id>。
第四,客户端要支持 ASK 重定向。Jedis、Lettuce、redis-py-cluster 等主流客户端都支持,但部分老版本或自研客户端只处理 MOVED 不处理 ASK,扩容时会报错。上线前应在预发环境验证一次完整 reshard 流程。
cluster nodes 输出纳入配置管理,扩容后重新生成拓扑快照。最后给出一个可复用的扩容检查清单,按顺序执行即可覆盖本节全部关键动作:
| 阶段 | 检查项 | 判断依据 |
|---|---|---|
| 加入前 | 新节点 cluster-enabled、端口、密码一致 | 能启动且 cluster info 可读 |
| 加入 | CLUSTER MEET 后 known_nodes +2 | cluster nodes 出现新 IP |
| 分配 | reshard 指定 from/to/slots | 输出 Moving slot 日志 |
| 迁移 | slots_assigned=16384,pfail/fail=0 | cluster info 无异常 |
| 完成 | 各主槽位≈4096,check 无 ERR | --cluster check 通过 |
| 收尾 | M4 从节点就位,客户端 ASK 正常 | 抽样读写无重定向残留 |
把上述流程走通一次,就能把“新增主节点并重新平衡槽位”从概念变成可重复执行的工程动作。真正决定扩容成败的,往往不是命令本身,而是对迁移中间态、ASK 重定向和从节点配套的提前准备。