分片节点下线且迁移未完成时,必须人工干预:先停均衡器,再查config.migrations中state为catching_up/cloning/committing且超30分钟无变化的记录并删除,最后对对应集合执行cleanupOrphaned命令清理孤儿chunk。

分片节点下线时数据没迁完,不能等它自己恢复,必须人工干预——否则 config 元数据会卡在半提交状态,后续所有 chunk 操作都可能失败。
怎么确认迁移卡在哪个阶段
只看 sh.status() 不够,它不反映实时迁移进度。关键要看 config.migrations 集合:
- 连任意
mongos,执行db.getSiblingDB("config").migrations.find({ "state": { $in: ["catching_up", "cloning", "committing"] } }).toArray() - 如果返回结果里有文档且
secs_running超过 30 分钟没变化,基本就是卡死了 - 特别注意
"state": "catching_up":这阶段依赖源分片 oplog 同步进度,若 secondary 落后太多,就会一直挂住
目标分片已不可恢复时的清理步骤
如果下线节点磁盘损坏、配置丢失或无法拉起,就不能指望自动续传。必须直连 config 副本集主节点操作:
- 先停均衡器:
sh.stopBalancer(),避免新迁移干扰判断 - 删掉未完成的迁移记录:
db.migrations.deleteMany({"state": {$in: ["catching_up","cloning","committing"] } }) - 对已标记
"state": "committed"但目标分片不存在的记录,先查config.chunks确认该 chunk 还在源分片上(sh.status()显示归属未变),再手动删除对应 migration 文档 - 最后运行
db.runCommand({cleanupOrphaned: "db.collection"})(替换为实际库名和集合名),清理元数据与物理数据不一致的孤儿 chunk
为什么不能直接重启分片或 kill mongos
重启下线节点本身不会修复元数据错位;在 mongos 上执行 db.killOp() 对分片内部的 moveChunk 无效——命令必须发给正在执行迁移的那个源分片的 primary 节点。
更危险的是:如果只是简单删掉 config 库里的 migration 记录却不跑 cleanupOrphaned,chunk 在源分片上仍存在,但 config 认为它该在目标分片,下次 balance 就会报 ConflictingOperationInProgress 或触发错误的双写。
真正容易被忽略的是:cleanupOrphaned 命令只作用于指定集合,且不会跨库自动扫描——你得知道具体哪个库哪个集合残留了问题 chunk,漏掉一个,后续就可能出数据不一致。


















