3. 槽位迁移与重新分片操作

Redis Cluster 把整个键空间划分为 16384 个槽(slot),每个槽由某个主节点负责。当新增节点、下线节点或各节点负载不均时,就需要把一部分槽从源节点迁移到目标节点,这个过程称为重新分片(resharding)。Redis 提供了 redis-cli --cluster reshard 命令,可以在线完成槽迁移,期间集群仍可对外提供服务,但会伴随键的搬迁和客户端重定向。

理解本节的关键在于三个层面:迁移的操作流程、迁移过程中键空间的变化、以及客户端在迁移期间遇到的重定向行为。下面先看整体数据流与状态变化。

槽迁移数据流与客户端重定向 源节点 M1 负责槽 0-5460 槽 100 状态:MIGRATING 键 key:A → 已迁移 键 key:B → 待迁移 目标节点 M4 新增节点,暂无槽 槽 100 状态:IMPORTING 键 key:A → 已接收 键 key:B → 等待中 MIGRATE key:A 客户端 Client 请求 key:B(槽 100) ASK 重定向 MOVED 重定向 状态说明: MIGRATING:源节点槽状态,键逐步迁出 IMPORTING:目标节点槽状态,键逐步迁入 待迁移键:尚未完成搬迁

一、reshard 命令的执行流程

执行 redis-cli --cluster reshard 时,工具会以交互方式询问几个关键参数:目标节点 ID、要迁移的槽数量、源节点(可指定一个或多个,也可输入 all 让集群自动从各节点均匀抽取)。确认后,工具会生成迁移计划并逐个槽执行。

redis-cli --cluster reshard 192.168.1.10:7000 \
  --cluster-from <源节点ID> \
  --cluster-to <目标节点ID> \
  --cluster-slots 1000 \
  --cluster-yes

其中 --cluster-slots 1000 表示迁移 1000 个槽,--cluster-yes 跳过交互确认。工具内部对每个槽执行的核心动作是:

  1. 在目标节点上对槽执行 CLUSTER SETSLOT <slot> IMPORTING <source-id>;
  2. 在源节点上对槽执行 CLUSTER SETSLOT <slot> MIGRATING <target-id>;
  3. 用 CLUSTER GETKEYSINSLOT <slot> <count> 分批取出该槽内的键;
  4. 对每个键执行 MIGRATE <target-ip> <target-port> <key> 0 <timeout>,把键值连同 TTL 一起搬到目标节点;
  5. 键全部迁完后,向集群中所有主节点广播 CLUSTER SETSLOT <slot> NODE <target-id>,完成槽归属权的正式切换。

二、迁移过程中的键空间变化

在槽处于 MIGRATING/IMPORTING 状态期间,该槽的键会同时分布在两个节点上:已迁移的键存在于目标节点,未迁移的键仍留在源节点。此时源节点上该槽的键数量持续减少,目标节点上持续增加。可以用以下命令观察:

# 查看源节点上该槽剩余的键数量
redis-cli -p 7000 cluster countkeysinslot 100

# 查看目标节点上该槽已迁入的键数量
redis-cli -p 7003 cluster countkeysinslot 100

当 countkeysinslot 在源节点返回 0 时,说明该槽的键已全部迁出,随后槽归属切换完成。整个过程中,键空间总量不变,只是分布发生了变化。

三、客户端重定向:ASK 与 MOVED

迁移期间客户端访问该槽的键时,会遇到两种重定向:

  • ASK 重定向:当客户端请求的键尚未迁移、但槽已标记为 MIGRATING 时,源节点发现该键不在本地,会返回 ASK <slot> <target-ip>:<port>,要求客户端临时到目标节点执行一次 ASKING 后再发原命令。ASK 是一次性的,不改变客户端缓存的槽映射。
  • MOVED 重定向:当槽的归属权已正式切换给目标节点后,源节点(或任何仍持有旧映射的节点)会返回 MOVED <slot> <target-ip>:<port>,客户端应永久更新本地槽映射表,后续该槽的请求直接发往目标节点。
ASK 是迁移中的临时跳转,MOVED 是迁移完成后的永久改向。客户端库(如 Jedis、Lettuce、redis-py-cluster)会自动处理这两种重定向,但理解其区别有助于排查迁移期间的超时和延迟抖动。

四、实操观察与结果判断

下面给出一个可复现的观察流程(示例参数,非实测数据):假设集群有 M1(7000)、M2(7001)、M3(7002) 三个主节点,新增 M4(7003),计划从 M1 迁移 1000 个槽到 M4。

# 1. 查看迁移前各节点槽分布
redis-cli -p 7000 cluster nodes | grep master

# 2. 执行 reshard
redis-cli --cluster reshard 192.168.1.10:7000 \
  --cluster-from <M1-id> --cluster-to <M4-id> \
  --cluster-slots 1000 --cluster-yes

# 3. 迁移过程中观察键数量变化
watch -n 1 'redis-cli -p 7000 cluster countkeysinslot 100'

# 4. 迁移完成后确认槽归属
redis-cli -p 7000 cluster slots | grep -A2 100

判断迁移是否成功的依据:cluster nodes 输出中 M4 的槽范围包含目标槽;源节点上该槽的 countkeysinslot 为 0;客户端不再收到该槽的 ASK/MOVED 重定向(可用 redis-cli -p 7000 --stat 或客户端日志观察)。

迁移大槽(键数量多)时,MIGRATE 会阻塞源节点和目标的对应分片,可能造成毫秒到秒级延迟。建议在业务低峰执行,并通过 --cluster-slots 分批迁移,避免一次性搬迁过多键。同时确保 cluster-node-timeout 足够大,防止迁移期间误判节点下线。

掌握 reshard 的流程、键空间变化和 ASK/MOVED 重定向机制后,就能在扩容、缩容和负载均衡场景中安全地完成在线槽迁移,并快速定位迁移期间的客户端异常。