必须手动执行两次removeshard命令才能安全移除分片:首次启动排空,待remaining.chunks和dbs均为0后再次执行,才触发元数据清理并返回"stage":"completed";卡在"ongoing"主因是未分片数据库仍绑定该分片为primary,须先用moveprimary或dropdatabase解除。

必须先排空数据再清理元数据,否则 removeshard 会卡在 "state":"ongoing" 永不结束。
为什么 removeshard 一直卡在 draining ongoing
这不是迁移慢,而是有未分片数据库(unsharded database)仍把该分片设为 primary。即使所有分片集合的 chunk 都已迁走,removeshard 也会阻塞。
- 运行
db.printShardingStatus(),检查每个数据库的"primary"字段是否指向待删分片 - 常见“隐身”数据库包括
admin、config、logs或业务自建但未分片的库 - 如果发现绑定,必须用
movePrimary或dropDatabase解除,不能跳过
执行 removeshard 的正确姿势
removeshard 是两阶段操作:第一阶段启动排空,第二阶段清理 config 元数据。MongoDB 不自动触发第二阶段,必须手动再执行一次。
- 首次执行返回
"state":"started"或"state":"ongoing",说明排空已启动 - 当
remaining: { chunks: 0, dbs: 0 }后,**必须再次执行同一命令**,才会进入清理阶段 - 最终成功标志是返回
"stage":"completed",此时才能安全关闭该节点的mongod进程 - 命令必须在
mongos上的admin数据库中执行;直连mongod会报not authorized
删完后节点重启又自动注册?清空 dbpath 是硬要求
被移除的分片节点本地磁盘上残留旧集群元数据(如 shardIdentity.bson、_mdb_catalog_version、WiredTiger 日志等),会导致它拒绝加入新集群或无法写入。
- 停掉该节点的
mongod进程 - 清空整个
--dbpath目录:rm -rf /path/to/dbpath/*(不是只删data/子目录) - 重启前,在所有
mongos上执行db.runCommand({ flushRouterConfig: 1 }),避免路由缓存残留 - 重加时用
sh.addShard("shard_name/host:port"),不要复用旧配置或旧 hostname
config server 里还有残留记录怎么办
即使 removeshard 返回 "stage":"completed",sh.status() 仍显示该分片,说明 config.shards 或 config.databases 中仍有引用。
- 连到
config副本集主节点(不是mongos),执行use config - 查
db.shards.findOne({ _id: "shard_name" }),若存在则db.shards.deleteOne({ _id: "shard_name" }) - 查
db.databases.find({ "primary": "shard_name" }),若有结果,先movePrimary再删 - 别漏掉
config.migrations里state: "committed"且to: "shard_name"的记录——它们可能让均衡器反复报ConflictingOperationInProgress
真正容易被忽略的是:movePrimary 和 removeshard 都依赖 balancer 处于活跃状态,而 balancer 默认只在 UTC 22:00–06:00 运行;白天执行却没调 sh.setBalancerWindow(),就等于干等。











