cleanupOrphaned命令必须直连shard primary执行,不支持mongos或secondary;需停balancer、确认无迁移、校验集合存在,仅清理绝对孤儿文档。

cleanupOrphaned 命令必须直连 shard primary 执行
mongos 会直接拒绝该命令,报错 command cleanupOrphaned not supported from mongos。它不是路由型命令,也不走 config server 协调,必须用 mongo --host 直连每个 shard 的 primary 节点(不能连 secondary,否则命令看似成功但 stoppedAtKey 始终为 null,实际没清理)。
常见错误包括:
- 误连 mongos 或 config server,返回
{"ok":0}却无提示,以为失败或成功都难判断 - 账号权限不足时返回
"codeName" : "Unauthorized",但不说明缺clusterAdmin或root - 脚本中未校验连接目标类型,自动化时批量连错节点,日志里全是静默无效操作
cleanupOrphaned 不接受 startingFromKey 参数(8.0+ 已废弃)
传 startingFromKey 在 MongoDB 8.0 及以上版本完全无效,官方已标记为 deprecated。命令实际行为是:扫描整个命名空间(即指定集合的全部数据),异步比对分片键值是否落在当前所有 chunk 的 min/max 范围内;仅当值完全越界时才判定为孤儿并删除。
这意味着:
- 无需构造分页逻辑,旧版 JS 脚本里的
while (nextKey != null)循环纯属冗余 - 无法“从某 key 开始清理”,也无法跳过部分范围——要么全扫,要么不扫
- 执行时间取决于集合大小和磁盘 I/O,大集合可能耗时数分钟甚至更久,期间阻塞该 shard 上其他写操作
执行前必须停掉 balancer 并确认迁移状态
若 balancer 正在运行,或有 chunk 迁移处于 in progress 状态,cleanupOrphaned 会直接失败,返回 "errmsg" : "cannot cleanup orphaned data while balancing"。
安全操作路径是:
- 先在 mongos 上运行
sh.stopBalancer(),并用sh.getBalancerState()和sh.isBalancerRunning()双重确认 - 检查
config.migrations是否为空,以及config.chunks中目标集合的 chunk 边界是否连续、无重叠 - 避免在业务高峰期执行,尤其不要在
moveChunk刚结束的 15 分钟内立刻清理——此时orphanCleanupDelaySecs机制可能还在后台异步处理
它只清理“绝对孤儿”,不处理伪孤儿和写入错误
cleanupOrphaned 的判定逻辑极其严格:仅删除那些分片键值 不在任何 chunk 的 min/max 范围内 的文档。它不会管你是不是写错了分片键类型(比如把 NumberLong("123") 写成 NumberInt(123)),也不会处理应用层绕过 mongos 直连 shard 写入的脏数据。
这类“伪孤儿”需要人工介入:
- 用
db.collection.find().explain(true)查看chunkSkips字段,数值 > 0 是强信号 - 手动连各 shard,按分片键值查同一条记录是否重复出现
- 结合
config.chunks.find({ns: "db.coll"}).sort({min: 1})输出边界快照,比对异常值
真正容易被忽略的是:这个命令不校验集合是否存在——传错 "users" 和 "user" 都返回 {"ok":1},但什么都不会发生。生产环境务必先 db.getSiblingDB("db").getCollectionNames() 核对名称。











