removeshard成功执行的前提是启用负载均衡器且清空未分片集合:需确保sh.getbalancerstate()和sh.isbalancerrunning()均返回true,否则会卡在draining阶段;未分片集合须删除或迁移(v8.0+支持);完成后须彻底清理原分片节点数据目录及config.shards记录。

必须先确保负载均衡器(balancer)处于启用状态,否则 removeShard 会卡在“draining”阶段永远不完成。 这是最常被忽略的前提,不是可选项,是硬性依赖。
为什么 removeShard 会一直卡在 draining 状态
根本原因是 MongoDB 不会主动触发数据迁移 —— 它只依赖 balancer 周期性扫描并搬走 chunk。如果 balancer 被手动停过(比如执行过 sh.stopBalancer()),或因配置问题未启动,removeShard 就只能等待,不会报错也不会超时。
- 检查 balancer 是否运行:
sh.getBalancerState()返回true才算启用 - 确认 balancer 没被禁用:
sh.isBalancerRunning()必须返回true - 如果 balancer 处于关闭状态,直接执行
sh.startBalancer(),不要跳过这步 - 注意:某些旧版本(如 v3.6)中,
sh.isBalancerRunning()可能返回空值,此时应以sh.getBalancerState()为准
执行 removeShard 前必须清空未分片集合
未分片集合(即没执行过 sh.enableSharding() 和 sh.shardCollection() 的库内集合)默认只存在 primary 分片上,removeShard 不会自动迁移它们 —— 会导致命令阻塞在 “waiting for unsharded collections to be dropped or moved”。
- 列出所有未分片集合:
use config; db.collections.find({ "dropped": false, "key": { $exists: false } }) - 对每个未分片集合,要么删掉:
db.getSiblingDB("DB_NAME").COLLECTION_NAME.drop() - 要么(从 MongoDB 8.0 起)手动迁移:
sh.moveCollection("DB_NAME.COLLECTION_NAME", "target_shard_id") - 注意:v7.x 及更早版本不支持
sh.moveCollection()操作未分片集合,只能删除或升级
removeShard 执行后,原分片节点不能直接复用
即使命令返回 { "msg": "removeshard completed successfully", "ok": 1 },该分片的 mongod 进程仍保留集群元数据(如 shardIdentity 文档),若直接加回集群,会引发配置冲突或拒绝加入。
- 必须彻底清理:停止该节点的
mongod,删除其数据目录下的_mdb_catalog_version、collection-*.wt、index-*.wt等文件(或直接清空整个dbPath) - 尤其注意 config server 的
shards集合是否已移除对应记录:use config; db.shards.findOne({ _id: "shard_id" })应返回空 - 重启前务必确认该节点不再出现在
sh.status()输出中 —— 否则说明元数据残留,强行重启可能污染集群状态
整个过程最耗时的环节其实是数据迁移本身,而非命令执行;chunk 数量、网络带宽、目标分片磁盘 IO 都会影响 draining 时间。别盯着命令返回,要盯 sh.status() 里该分片的 draining: true 是否消失,以及 chunks 计数是否归零。











