目标分片宕机致迁移卡在commitChunkMove阶段,需先停均衡器、清理config.migrations中未完成记录,并执行cleanupOrphaned命令修复元数据不一致。

目标分片宕机导致迁移卡在 commitChunkMove 阶段
迁移过程中目标分片(shard)意外下线,mongos 会持续重试,但最终在 config.shards 和 config.chunks 中留下“半提交”状态:源分片仍标记该 chunk 为 owned,目标分片却没真正写入数据,且 config.migrations 里残留未完成的迁移记录。此时不能简单重启分片了事——chunk 元数据不一致,后续 balance 或手动 moveChunk 可能报 ChunkTooBig 或 ConflictingOperationInProgress。
实操建议:
- 先确认目标分片是否已彻底不可恢复;若只是临时宕机且能快速拉起,优先等它上线,MongoDB 通常会在 5–10 分钟内自动续传(依赖
moveChunk的幂等性) - 若目标分片已永久失效(如磁盘损坏、配置丢失),必须人工干预:登录任意
mongos,用sh.stopBalancer()暂停均衡器,避免自动触发新迁移干扰判断 - 检查
config.migrations中状态为"state" : "committed"但目标分片不可达的记录——这类记录实际未落地,需删除;而"state" : "catching_up"或"cloning"的则说明数据根本没传完,可安全清理
清理 config.migrations 和孤儿 chunk 元数据
冗余迁移记录本身不占磁盘空间,但会阻塞后续 chunk 操作。重点不是删记录,而是让 config server 认为“这事没发生过”。
实操建议:
- 连接到
config副本集主节点(非 mongos),执行:db.migrations.deleteMany({ "state": { $in: ["catching_up", "cloning", "committing"] } })—— 这些状态代表迁移未抵达最终阶段,删掉不影响数据一致性 - 对已标记
"state": "committed"但目标分片不存在的记录,先查对应min/max范围:db.chunks.findOne({ "min": { $gte: ... }, "max": { $lte: ... } }),确认该 chunk 在源分片上仍完整存在(即sh.status()显示它还在原 shard),再删 migration 记录 - 运行
db.runCommand({cleanupOrphaned: "db.collection"})(替换为实际库和集合名)——这个命令只清理“元数据说 chunk 在某 shard,但该 shard 上查不到对应数据”的情况,不会误删有效数据
目标分片恢复后如何避免重复迁移或数据错乱
如果目标分片只是短暂宕机后重新加入集群,它本地可能残留部分未清理的临时文件(如 _tmp_moveChunk_* 目录),也可能因 journal 回滚导致部分 chunk 数据处于中间态。
实操建议:
- 启动分片前,进入其数据目录(如
/data/db/),手动删除所有匹配_tmp_moveChunk_*的子目录和文件——MongoDB 不会自动清理这些中断产生的临时文件 - 启动分片进程时,加上
--nojournal(仅限紧急恢复且确认无其他写入)或确保storage.journal.enabled: true已开启,否则 crash 后可能无法回滚到一致点 - 分片上线后,立刻在
mongos执行:sh.startBalancer(); sh.balanceOff(); sh.setShardingVersion();
—— 最后一条强制刷新所有分片的版本号,防止因 version mismatch 导致 chunk 元数据同步失败
为什么不能直接删目标分片上的 _tmp_moveChunk_* 文件而不重启服务
这些临时目录由 moveChunk 流程创建,但 MongoDB 进程在运行中会持有对其的文件句柄。Linux 下虽允许“删文件但不释放空间”,但 MongoDB 可能继续往已被 unlink 的 inode 写数据,造成磁盘空间不释放、日志混乱,甚至下次迁移时复用同名临时路径引发冲突。
实操建议:
- 必须停掉该分片的
mongod进程后再删_tmp_moveChunk_*;用lsof -p $(pgrep mongod) | grep tmp可验证是否还有残留句柄 - 删完重启时加
--repair(仅适用于 WiredTiger 引擎且无活跃写入)可校验集合级一致性,但耗时长,生产环境慎用;更稳妥的是用db.collection.validate(true)对关键集合做逻辑校验 - 真正容易被忽略的是:即使删了临时文件、清了 migration 记录,如果源分片上该 chunk 的
lastmod时间戳比 config server 的新,balance 仍可能再次尝试迁移——此时需手动调用sh.moveChunk()指定{ "_id" : "force" }强制跳过版本检查(仅调试用)


















