空块堆积本质是“元数据说 chunk 在某分片,但该分片上查不到对应数据”,cleanupOrphaned 是唯一安全、内置的修复手段,不能靠删 config.chunks 或手动 drop collection 解决。

直接结论:空块堆积本质是“元数据说 chunk 在某分片,但该分片上查不到对应数据”,cleanupOrphaned 是唯一安全、内置的修复手段,不能靠删 config.chunks 或手动 drop collection 解决。
为什么 cleanupOrphaned 能解决空块堆积问题
空块堆积常见于 moveChunk 中断后:源分片已清空 chunk 数据,但 config.chunks 仍记录该 chunk 属于它;或目标分片写入失败,config.chunks 却标记归属目标分片。这些“有元数据、无实际数据”的 chunk 就是孤儿块(orphaned chunks)。cleanupOrphaned 命令会扫描目标分片上所有 chunk 范围,对比 config.chunks 记录,只删除那些“元数据声称属于我,但我本地没数据”的文档——不碰元数据,不改路由表,只清理真实冗余。
常见错误现象包括:sh.status() 显示某分片 chunk 数异常高但 db.collection.countDocuments() 返回 0;查询该分片上的 chunk 范围返回空结果;后续 moveChunk 报 ConflictingOperationInProgress 或 ChunkTooBig。
- 该命令必须在目标分片的 mongod 实例上执行(不是 mongos),且需连接到该分片的主节点
- 仅作用于指定库和集合,例如
db.runCommand({cleanupOrphaned: "mydb.mycoll"}) - 不会影响其他分片、不阻塞集群读写,但会短暂占用该分片 I/O
执行 cleanupOrphaned 前必须确认的三件事
盲目运行 cleanupOrphaned 可能误删有效数据,尤其当 chunk 边界被手动修改过或存在自定义分片键时。
- 确认目标分片当前是
PRIMARY状态(用rs.status()检查),避免在 secondary 上执行 - 用
sh.status()找出疑似空块所在的库/集合,并核对config.chunks中该 chunk 的min/max和shard字段是否指向当前分片 - 在该分片上手动验证:执行
db.mycoll.find({,若真为空才适合触发清理: {$gte: , $lt: }}).limit(1)
注意:cleanupOrphaned 不接受正则或通配符,必须指定完整库名和集合名;若集合名含特殊字符,需用引号包裹字符串。
cleanupOrphaned 和 deleteMany(config.chunks) 的根本区别
有人试图直接删 config.chunks 文档来“清除空块”,这是高危操作:缺失 chunk 记录会导致所有对该 chunk 范围的查询路由失败,mongos 返回 ShardKeyNotFound 或静默丢弃请求。
-
cleanupOrphaned是只读元数据 + 写实际数据的操作:它读 config.chunks,再删目标分片上多余文档,不碰 config 数据库 -
db.config.chunks.deleteMany(...)是直接破坏路由表的行为,等同于拔掉集群的导航图 - 若 config.chunks 本身已错乱(如
state字段异常),应先用db.migrations.deleteMany()清理迁移记录,再跑cleanupOrphaned
性能影响方面:cleanupOrphaned 扫描范围取决于 chunk 大小和索引效率,大集合建议在低峰期执行;它不触发全表扫描,而是利用分片键索引快速定位边界。
空块清理后仍报错?检查 placement history 是否被破坏
MongoDB 6.0+ 引入了 placement history(放置历史)用于快照读和事务一致性。如果之前执行过 defragmentCollection 或强制中断迁移,placement history 可能丢失,导致 cleanupOrphaned 成功后仍出现 StaleChunkHistory 错误。
- 此时不要重试
cleanupOrphaned,它不修复 history - 观察
sh.isBalancerRunning()是否为 true;若为 false,手动sh.startBalancer()触发一次元数据同步 - 等待约 5 分钟,让各分片从 config server 拉取最新 history;期间快照读可能失败,属预期行为
真正容易被忽略的是:空块堆积常与磁盘空间不足共存。执行 cleanupOrphaned 前务必确认目标分片剩余空间 >10%,否则命令会因无法分配临时 buffer 而卡住或失败。











