sh.removeShard() 卡在“draining”状态是因为需先迁移完所有chunk,常见原因包括目标分片磁盘满、网络分区、chunk分布不均或存在未分片集合;需检查balancer运行、手动迁移未分片数据并刷新路由配置。

sh.removeShard() 为什么卡在 “draining” 状态不动
因为 MongoDB 不是直接删分片,而是先触发数据迁移,把该分片上所有 chunk 迁走;只要还有 chunk 没搬完,状态就一直是 draining。常见卡住原因有:目标分片磁盘满、网络分区、chunk 分布严重不均、或者某个集合没开启分片(sh.status() 里看不到它,但数据真在那个分片上)。
实操建议:
- 用
sh.status()确认目标分片名(别输错,大小写敏感) - 执行
sh.removeShard("shard01")后,立刻查db.adminCommand({ getShardMap: 1 }),看draining是否变为true - 盯住
db.printShardingStatus()输出里的balancer和各 shard 的chunks数,每 30 秒刷一次,观察是否真在减少 - 如果 chunks 数卡住不变,检查
mongos日志里有没有Failed to move chunk或NotMaster类错误
数据排空失败的三个典型场景和对应操作
排空不是自动成功的——它依赖均衡器(balancer)、副本集健康度、以及集合是否真正分片。很多“删不掉”的问题其实出在误判了数据归属。
实操建议:
- 确认所有集合都已分片:
db.getCollectionNames().forEach(n => print(n + ": " + sh.isSharded(n))),返回false的集合不会参与迁移,得手动导出再导入到其他分片 - 检查是否有未分片的数据库:比如
admin、config或业务自建的logs库,它们的数据默认落在主分片,但sh.removeShard()不管这些 —— 得用mongodump+mongorestore --uri="mongodb://other-shard:27018"手动搬 - 如果某集合启用了 zone sharding,而目标分片被划在某个 zone 里,那它的 chunk 就不会被迁出:用
sh.getShardDistribution()查分布,再用sh.removeTagRange()临时解绑 tag
移除过程中要不要关 balancer
不需要关,也**不应该关**。balancer 是排空的执行者,关了它,sh.removeShard() 就彻底停摆。但要注意:balancer 默认只在维护窗口运行(UTC 22:00–06:00),如果你白天执行,它可能等到半夜才开始搬数据。
实操建议:
- 临时放开 balancer 时间窗:
sh.setBalancerState(true)+sh.setBalancerWindow({open: "00:00", close: "23:59"}) - 迁移期间避免手动调用
sh.moveChunk(),容易和 balancer 冲突,导致 chunk 元数据不一致 - 如果集群有多个分片正在 draining,balancer 会串行处理,一个一个来 —— 别以为它卡了,其实是排队中
sh.removeShard() 成功后还要做什么
命令返回 "msg" : "removeshard completed successfully" 只代表 config server 已删除元数据,不代表物理节点能立刻下线。残留风险在于:旧分片仍可能被 mongos 缓存为可用节点,或副本集成员还在心跳列表里。
实操建议:
- 停掉目标分片的
mongod进程前,先在所有mongos上执行db.runCommand({ flushRouterConfig: 1 }),清掉路由缓存 - 检查
config.shards集合是否真的删掉了对应文档:db.shards.find({ _id: "shard01" })应无结果 - 如果该分片是副本集,记得从其他成员的
rs.conf().members里手动rs.remove("old-host:27018"),否则下次初始化会连不上 - 最后再关进程、卸载数据目录 —— 顺序错了,config server 可能报
ShardNotFound错误
最常被跳过的一步是刷新路由配置,很多人看到命令返回 success 就直接 kill -9,结果第二天发现某些查询 500 了,查日志才发现 mongos 还在往已下线节点发请求。










