Redis集群槽位迁移不阻塞读写,但槽状态未对齐、客户端缓存未刷新或CLUSTER SETSLOT顺序错误会导致MOVED/ASK错误;须成对设置IMPORTING/MIGRATING状态,分批迁移key并合理设timeout,确保槽闭环、客户端及时刷新、旧节点延时下线。

只要客户端用的是 RedisCluster 类(phpredis)或 RedisCluster(redis-py),且集群拓扑变更符合 Redis 协议规范,迁移本身就不会阻塞读写——真正卡住业务的,往往是槽位状态没对齐、客户端缓存未刷新、或迁移命令顺序错了。
CLUSTER SETSLOT 导入/导出状态必须成对设置
迁移单个槽时,必须先在目标节点执行 CLUSTER SETSLOT {slot} IMPORTING {source-node-id},再在源节点执行 CLUSTER SETSLOT {slot} MIGRATING {target-node-id}。漏掉任一端,后续 MIGRATE 命令会直接报错 ERR Target node is not in importing state 或 ERR Source node is not in migrating state。
常见错误现象:
- 迁移卡在某一个槽,
CLUSTER NODES显示该槽处于[{slot}- 或[{slot}->-...]状态但不推进 - 客户端开始大量返回
MOVED或ASK错误,但重试后仍失败
实操建议:
- 用
redis-cli -c -h {node} -p {port} cluster nodes检查迁移中槽的状态是否成对出现(源节点有->{slot},目标节点有{slot}) - 不要跳过
IMPORTING/MIGRATING步骤,试图靠CLUSTER REHASH自动完成——它只适用于某些旧版本或特定工具链 - 批量迁移多个槽时,建议按每 10~20 个槽为一组,组内严格顺序执行:先全设
IMPORTING→ 再全设MIGRATING→ 最后逐槽MIGRATE
phpredis 的槽缓存 TTL 会掩盖拓扑变更延迟
RedisCluster 类默认将 CLUSTER SLOTS 结果缓存 30 秒(硬编码在 cluster_library.c 中)。这意味着即使你已把槽迁完并执行了 CLUSTER SETSLOT {slot} NODE {new-node-id},客户端仍可能在未来 30 秒内继续往旧节点发请求,触发 MOVED 重定向——这不是 bug,是设计使然。
影响点:
- 迁移刚完成时,部分请求走旧路径,增加网络跳转开销
- 若旧节点在迁移后立即下线,这些请求会失败,表现为偶发
Connection refused
实操建议:
- 迁移完成后,主动调用
$cluster->flushSlots()(phpredis ≥ 5.3.4)强制刷新本地槽映射 - 或改用
RedisCluster::OPT_REFRESH_STALENESS选项(需 phpredis ≥ 6.0),让客户端在收到MOVED后自动更新槽表 - 避免在迁移窗口末尾立刻关停旧节点;建议保留至少 60 秒观察期,并监控
redis-cli --cluster check输出是否稳定为 OK
大 key 和 pipeline 批量迁移必须配合 count 参数
执行 MIGRATE {target-ip} {target-port} "" 0 {timeout} KEYS {key1 key2 ...} 时,如果传入的 key 列表过大(比如单次传 1000+ 个 key),目标节点可能因内存分配或命令队列积压而超时,导致迁移中断并残留部分 key。
关键参数差异:
-
count在CLUSTER GETKEYSINSLOT {slot} {count}中控制每次拉取多少 key,不是总数量 -
timeout是单次MIGRATE命令的 socket 超时,不是整个槽的迁移超时 - Redis 3.0.6+ 支持批量 key 迁移,但老版本只能单 key
MIGRATE,性能极低
实操建议:
- 对每个槽,先用
CLUSTER GETKEYSINSLOT {slot} 100分批获取 key,再用MIGRATE ... KEYS每次传 50~100 个 key - 超时值设为
5000(5 秒)起步,若目标节点负载高可升至15000;低于1000容易误判失败 - 遇到
NOKEY响应时不必 panic——说明该槽当前无 key,可跳过后续步骤
真正决定迁移是否“安全”的,不是命令有没有报错,而是槽状态是否闭环、客户端是否及时感知、以及旧节点是否在缓存失效窗口外才下线。这三个环节里任意一个断开,都会让“在线”变成“伪在线”。











