Redis Cluster 新增节点 slot 迁移卡住:"open slots" 故障复盘
# 1. 现象
给 Redis Cluster 加节点、做 slot 迁移(redis-cli --cluster reshard)的过程中,如果中途被打断(网络抖动、手动 Ctrl-C、迁移脚本异常退出),再执行集群健康检查会报错:
版本说明
本文基于 Redis Cluster 5.x/6.x 的 redis-cli --cluster 工具链,slot 状态机在 7.x 上未发生变化,仍适用。
redis-cli --cluster check <PUBLIC_IP>:8001
>>> Check for open slots...
[WARNING] Node <INTERNAL_IP>:8001 has slots in importing state 11763.
[WARNING] The following slots are open: 11763.
2
3
4
5
集群此时能正常读写(大部分 slot 状态正常),但这个 11763 slot 处于一种"悬空"状态,--cluster check 会持续报警,如果不处理,后续再做别的 reshard 操作大概率会失败或报更多冲突。
# 2. 定位:为什么会卡在"open slots"
Redis Cluster 迁移一个 slot 分两步——源节点标记为 MIGRATING,目标节点标记为 IMPORTING,等这个 slot 里所有 key 都从源节点搬到目标节点后,再把 slot 的归属正式切给目标节点(状态变回 stable)。
根因:如果迁移在"key 已经搬完但状态还没切换成 stable"或"搬到一半就中断"这个中间态被打断,slot 就会一直卡在 importing(或对应地在源节点卡在 migrating),这是 Redis Cluster 迁移协议本身的设计——它不会自动回滚或自动完成,需要人工介入确认这个 slot 到底应该属于哪个节点,再手动收尾。
# 2.1 定位步骤详解
第一步:找到"悬空"的 slot
redis-cli --cluster check <HOST>:<PORT> 2>&1 | grep -E "(importing|migrating|open slots)"
典型输出:
[WARNING] Node <TARGET_IP>:8001 has slots in importing state 11763.
[WARNING] Node <SOURCE_IP>:8001 has slots in migrating state 11763.
2
这里的 11763 就是需要人工介入的 slot。
第二步:判断 key 是否已搬空
先确认这个 slot 里的 key 是否已经搬空:
# 在目标节点(importing 状态)上检查
redis-cli -h <TARGET_IP> -p 8001 cluster countkeysinslot 11763
# 输出: (integer) 0
# 在源节点(migrating 状态)上检查
redis-cli -h <SOURCE_IP> -p 8001 cluster countkeysinslot 11763
# 输出: (integer) 0
2
3
4
5
6
7
0 表示 key 已经全部迁移完成,只是状态没有切换——这种情况可以直接安全收尾;如果这里不是 0,说明迁移没搬完,需要先用 redis-cli --cluster fix 或手动继续搬迁 key,不能直接强行改状态,否则会丢数据。
第三步:确认节点角色
# 查看节点状态,确认是哪个节点处于 importing 状态
redis-cli -h <ANY_NODE> -p 8001 cluster nodes | grep -E "11763|importing|migrating"
2
# 3. 修复:手动把 slot 状态切回 stable
# 3.1 修复操作步骤
确认 key 已搬空后(cluster countkeysinslot 返回 0),在目标节点上执行:
# 在目标节点(importing 状态)上执行
redis-cli -h <TARGET_IP> -p 8001 cluster setslot 11763 stable
OK
2
3
stable 是告诉这个节点"停止迁移状态、这个 slot 正式归你了"。
如果源节点上也残留了 migrating 状态,同样对源节点执行一次:
# 在源节点(migrating 状态)上执行
redis-cli -h <SOURCE_IP> -p 8001 cluster setslot 11763 stable
OK
2
3
# 3.2 自动修复工具
如果不确定哪个节点有问题,或者有多个 slot 卡住,可以使用 redis-cli --cluster fix 自动修复:
# 自动检测并修复悬空的 slot
redis-cli --cluster fix <HOST>:<PORT> --cluster-slots <SLOT_ID>
2
工具会提示你是否确认修复,输入 yes 即可。
# 3.3 批量修复脚本
如果集群中有多个 slot 卡住,可以使用以下脚本批量修复:
#!/bin/bash
# 批量修复 "open slots" 问题
HOST=$1
PORT=${2:-8001}
echo "=== 检查当前集群状态 ==="
redis-cli --cluster check ${HOST}:${PORT} 2>&1 | grep -E "(importing|migrating|open slots)"
echo -e "\n=== 尝试自动修复 ==="
redis-cli --cluster fix ${HOST}:${PORT}
echo -e "\n=== 验证修复结果 ==="
redis-cli --cluster check ${HOST}:${PORT} 2>&1 | grep -E "(OK|All 16384 slots)"
2
3
4
5
6
7
8
9
10
11
12
13
14
# 4. 验证
执行完修复后,运行以下命令验证集群已恢复正常:
redis-cli --cluster check <PUBLIC_IP>:8001
验证要点:
- 确认输出不再出现
open slots的 WARNING - 确认出现
[OK] All 16384 slots covered这类总结行 - 所有 slot 都有明确归属、集群拓扑一致
# 验证 slot 状态的完整检查
redis-cli -h <HOST> -p 8001 cluster info | grep cluster_slots_assigned
# 应该输出: cluster_slots_assigned:16384
# 验证所有节点的槽位状态正常
redis-cli -h <HOST> -p 8001 cluster info | grep cluster_state
# 应该输出: cluster_state:ok
2
3
4
5
6
7
# 5. 原理:Redis Cluster slot 迁移机制
# 5.1 迁移状态机
Redis Cluster 的 slot 迁移涉及三种状态:
| 状态 | 节点角色 | 含义 |
|---|---|---|
stable | 正常 | slot 归属明确,不再迁移 |
migrating | 源节点 | 正在将 slot 迁出,key 仍在该节点 |
importing | 目标节点 | 正在接收从其他节点迁入的 key |
# 5.2 迁移完整流程
- 启动迁移:在目标节点上执行
CLUSTER SETSLOT <slot> IMPORTING <source_node_id> - 标记源节点:在源节点上执行
CLUSTER SETSLOT <slot> MIGRATING <target_node_id> - 迁移 key:使用
MIGRATE命令逐个将 key 从源节点迁移到目标节点 - 切换归属:所有 key 搬完后,执行
CLUSTER SETSLOT <slot> NODE <target_node_id> - 状态复原:源节点和目标节点的 migrating/importing 状态自动清除,恢复
stable
# 5.3 中断场景分析
| 中断时机 | 结果 | 处理方式 |
|---|---|---|
刚执行 SETSLOT MIGRATING/IMPORTING,还未搬 key | 两节点处于中间态 | 两端都执行 setslot stable 回滚 |
| 正在搬 key 时中断 | 部分 key 已搬,部分还在源节点 | 用 redis-cli --cluster fix 继续搬完 |
key 全部搬完,还未执行 SETSLOT NODE | slot 悬空(importing/migrating 状态残留) | 执行 setslot stable 收尾 |
| 切换归属后中断 | 无影响,迁移动作已完成 | 不需要处理 |
# 6. 坑
不要在 key 还没搬完时就执行
setslot stable——这会导致这批未搬完的 key 在逻辑上"丢失"(既不在源节点的正常 slot 范围,也没真正进入目标节点),必须先用cluster countkeysinslot确认为 0。命令行里带
-a password会触发一条安全警告(Using a password with '-a' ... may not be safe)——这是提示密码可能出现在 shell 历史/进程列表里,不是操作失败,用--pass从环境变量或配置文件读取密码可以避免,或使用 Redis 6.0+ 的--no-auth-warning选项。迁移类操作尽量用
redis-cli --cluster reshard走官方工具链而不是纯手动逐条搬 key,工具会自动处理migrating/importing状态机的两端同步,减少中途中断留下悬空 slot 的概率。执行
SETSLOT <slot> STABLE不会删除源节点上残留的 key——如果源节点上还有未迁移走的 key,执行 stable 后这些 key 会变成"孤儿 key",无法通过正常的 slot 路由访问。建议在执行 stable 前再次确认源节点的 slot 内 key 数为 0。多 slot 并发迁移时中断,修复顺序有讲究——如果有多个 slot 卡住,应先修复还在迁移中(有 key 还在搬)的 slot,最后处理完全悬空的 slot。
网络分区期间的迁移状态——如果迁移过程中发生网络分区,importing 节点可能永远收不到源节点的数据,导致 slot 一直卡住。这种情况即使修复了状态,key 也可能不完整,需要结合 RDB 文件或备份进行数据核对。
# 7. 可复用要点
open slots报警的本质是迁移协议的中间态没有收尾,Redis 不会自动处理,需要人工判断并执行setslot stable。收尾前必须用
cluster countkeysinslot确认目标 slot 是否已经搬空——这是唯一能安全下断言的检查点。如果返回非 0,坚持用redis-cli --cluster fix继续迁移流程。优先用
redis-cli --cluster reshard而不是手动裸命令迁移,减少人为中断留下悬空状态的概率。修复后立即执行
redis-cli --cluster check验证集群完整性,确保所有 16384 个 slot 都有明确归属。生产环境建议:
- 迁移操作安排在业务低峰期
- 提前确认集群状态健康(无 fail 节点)
- 准备好监控告警,迁移过程中关注
cluster_stats_messages_received{type="migrate"}
# 8. 附录:常用命令速查
| 场景 | 命令 |
|---|---|
| 检查集群slot状态 | redis-cli --cluster check <host>:<port> |
| 查看某节点slot数 | redis-cli -h <host> -p <port> cluster countkeysinslot <slot> |
| 查看某slot归属 | redis-cli -h <host> -p <port> cluster nodes \| grep <slot> |
| 手动设置slot状态 | redis-cli -h <host> -p <port> cluster setslot <slot> stable |
| 迁移slot归属 | redis-cli -h <host> -p <port> cluster setslot <slot> node <node_id> |
| 自动修复工具 | redis-cli --cluster fix <host>:<port> |