removeShard卡在draining ongoing且chunks不减,说明迁移被阻塞,常见原因包括balancer关闭、窗口期过窄或目标分片写入能力不足;需检查balancer状态、扩宽窗口、验证目标分片磁盘与网络状况。

removeShard卡在draining ongoing,chunks数长期不减
这说明均衡器没真正把 chunk 迁走,不是命令没执行,而是迁移被阻塞了。最常见原因是 balancer 关着、窗口期太短,或者目标分片写入能力不足(比如磁盘满、网络不通、节点状态异常)。
实操建议:
- 先确认 balancer 是否开启:sh.getBalancerState() 返回 false 就得马上开:sh.startBalancer()
- 查窗口期:sh.getBalancerWindow(),如果只设了 "02:00-04:00" 这种窄窗,白天根本不动,建议临时扩成 "00:00-23:59"
- 检查 config.chunks 里该分片的 chunk 分布:db.getSiblingDB("config").chunks.find({shard: "shard_id"}).count(),再查这些 chunk 对应的 ns,看是不是集中在某个大集合上——如果是,可能 chunk 太大导致迁移失败,需先 splitChunk 或调大 chunkSize
- 登录目标分片,用 db.serverStatus().metrics.repl.buffer 看复制缓冲区是否积压,df -h 看磁盘剩余空间,mongostat 看 netIn/netOut 是否持续为 0
removeShard返回completed但sh.status仍显示该分片
这是典型的 config server 元数据残留。removeShard 命令只清理了部分记录,但 config.shards、config.databases 和 config.migrations 可能还有引用,导致集群“以为它还在”。
实操建议:
- 连 config 副本集主节点(不是 mongos),use config
- 查 db.shards.findOne({ _id: "shard_id" }),存在就删:db.shards.deleteOne({ _id: "shard_id" })
- 查有没有数据库还把它当 primary:db.databases.find({ primary: "shard_id" }),若有,必须先 sh.movePrimary("db_name", "other_shard"),再删 shards 记录
- 清 config.migrations 中已失效的记录:db.migrations.deleteMany({"state": {$in: ["catching_up","cloning","committing"]}}),对 "state": "committed" 的记录,先确认对应 chunk 在源分片上是否真实存在,再删
目标分片重启后自动重新注册进集群
说明原节点的 dbPath 里还存着 shardIdentity.bson 和其他元数据文件,mongod 启动时读到就尝试上报,结果被 config server 拒绝或引发冲突。
实操建议:
- 必须停掉该节点的 mongod 进程
- 不要只删 shardIdentity.bson —— 至少清空整个 dbPath,或至少删掉:_mdb_catalog_version、所有 collection-*.wt、index-*.wt、shardIdentity.bson
- 重启前,务必确认 sh.status() 和 db.runCommand({listShards: 1}) 输出里已无该 _id
- 如果之前用过 unshardCollection 或 reshardCollection,还要检查 config.collections 里有没有残留的 unsplittable 或 resharding 字段
cleanupOrphaned执行后仍有ConflictingOperationInProgress报错
这不是数据问题,是 config server 认为某次迁移“已完成”,但实际 chunk 没落盘,后续操作一碰就撞上这个“幽灵状态”。这类错误常出现在目标分片宕机后又恢复的场景。
实操建议:
- 先跑 db.runCommand({cleanupOrphaned: "db.collection"})(替换为真实库和集合名),它只清理“元数据说 chunk 在 A,A 上查不到”的情况
- 再查 config.chunks 里该集合的 chunk 是否都指向有效分片:db.getSiblingDB("config").chunks.find({ns: "db.collection"}).forEach(function(c){print(c.shard, db.getSiblingDB("config").shards.findOne({_id: c.shard})? "✓": "✗")})
- 如果发现 chunk 指向已删除分片,手动更新:db.getSiblingDB("config").chunks.updateMany({shard: "bad_shard_id"}, {$set: {shard: "good_shard_id"}})
- 最后强制刷新缓存:db.adminCommand({flushRouterConfig: 1}),让所有 mongos 重载路由表
配置项和路径容易被忽略:balancer 窗口、dbPath 清理范围、config.migrations 状态判断逻辑,三者任何一个漏掉,都会让 removeShard 表面成功、实际残血。

















