被锁定的Chunk实为迁移卡在catching_up/cloning等状态,需查config.migrations中state为catching_up、cloning或committing且超30分钟无变化的文档来确认;卡住主因是源副本集延迟、目标分片I/O瓶颈或chunk过大导致同步失败;安全终止须直连源分片primary执行db.killOp(),再运行cleanupOrphaned命令清理不一致数据。

被锁定的 Chunk 通常不是“锁住不动”,而是迁移卡在某个状态(比如 catching_up 或 cloning),导致后续所有涉及该 chunk 的操作(包括 balance、split、甚至某些聚合)被阻塞。核心问题在于迁移未完成,而非数据库加了某种语义锁。
怎么判断 chunk 是否真被卡住而不是正常迁移中
光看 sh.status() 不够,它只显示 chunk 分布,不反映实时迁移状态。必须查 config 库的 migrations 集合:
- 执行
db.getSiblingDB("config").migrations.find({ "state": { $in: ["catching_up", "cloning", "committing"] } }).toArray(),如果返回非空且长时间(>30 分钟)没变化,基本就是卡住了 - 特别注意
state: "catching_up":这阶段会持续拉取增量 oplog,若源 shard 副本集延迟高、或目标 shard 写入慢,就容易卡死 - 迁移卡住时,
sh.isBalancerRunning()可能仍返回true,但这只是说明 balancer 进程活着,不代表它在推进任务
为什么 moveChunk 会卡在 catching_up 状态
这不是配置错误,而是数据同步链路中的实际瓶颈。常见原因有三个:
- 源分片副本集 secondary 落后太多,
moveChunk在等 oplog 同步到足够位置才敢提交,但 secondary 一直追不上 - 目标分片磁盘 I/O 或 CPU 满载,写入 chunks 太慢,
catching_up阶段的批量插入持续超时 - chunk 本身太大(比如已超 200MB),而网络带宽有限,单纯传输+校验就耗时极长,期间任何一次心跳失败都会重试并延长状态
如何安全终止卡住的 moveChunk 操作
不能直接 kill mongos 或 mongod 进程,必须用 MongoDB 自身机制中断。关键点是:命令必须发给正在执行迁移的那个源分片的 primary:
- 先用
db.currentOp({ "secs_running": { $gt: 60 }, "command.moveChunk": { $exists: true } })找出对应的操作 ID(opid) - 再连上该源分片的 primary 节点(不是 mongos),执行
db.killOp(<code>opid) - 杀掉后,源分片上残留的 chunk 数据不会自动清理,需手动确认是否需要回滚;config 库里的 migration 记录会被自动标记为
failed,但 chunk 状态仍为jumbo或orphaned,得后续处理 - 切记:不要在 mongos 上执行
db.killOp(),它对分片内部的 moveChunk 操作无效
清理卡死后遗留的不一致状态
kill 掉迁移后,系统可能处于中间态:config 认为 chunk 已迁走,但源 shard 还留着数据,目标 shard 没收到全量——这就是孤儿文档的温床。必须立刻检查:
- 运行
db.runCommand({ cleanupOrphaned: "database.collection" }),该命令必须直连源 shard 的 primary 执行,不是 mongos - 注意:MongoDB 8.0+ 中
startingFromKey参数已废弃,命令会扫描整个命名空间,所以选业务低峰期运行 - 如果 cleanupOrphaned 报错说找不到 chunk 范围,说明 config 元数据已损坏,此时要先用
sh.refreshShardCatalog()强制重载分片路由表
真正难处理的从来不是“怎么停”,而是停完之后那一堆半成品状态——config 记录、分片本地数据、oplog 位点三者不同步,任何一步漏检都可能埋下数据不一致的隐患。


















