deletemany()后磁盘空间不降,因wiredtiger仅标记删除空间为“可复用”而非立即释放;compact命令会物理重写集合文件以回收空间,但需停机且仅限主节点、需充足磁盘空间及无长事务。

为什么 deleteMany() 后磁盘空间不下降?
MongoDB 的文档删除只是标记为“可复用”,并不立即归还磁盘空间。底层存储引擎(WiredTiger)会把删除的记录空间放入内部空闲列表,供后续插入复用——这叫“延迟回收”。所以即使你删掉 90% 数据,db.collection.stats() 显示的 size(实际数据大小)会降,但 storageSize(磁盘占用)几乎不变。
compact 命令到底在做什么?
compact 是 WiredTiger 层面的物理整理操作:它重写整个集合的数据文件和索引文件,丢弃所有已删除/过期的空间,合并碎片,并重新压缩。但它不是在线无锁操作——执行期间该集合**完全不可读写**(主节点会阻塞所有读写,副本集成员也会短暂中断同步)。
实操建议:
- 必须在维护窗口执行,且确保应用能容忍该集合数秒到数分钟不可用(取决于集合大小)
- 只对单个集合生效,不能跨库或通配符;语法是
db.runCommand({ compact: "collection_name" }) - 仅对 WiredTiger 引擎有效;MMAPv1 引擎已废弃,不支持
compact - 执行后
storageSize通常下降明显,但不会低于dataSize × 压缩率(WiredTiger 默认 zlib 压缩)
比 compact 更安全的替代方案有哪些?
如果无法接受停机,优先考虑这些渐进式方式:
-
collMod+usePowerOf2Sizes已弃用,别用;新版本靠storageEngine.wiredTiger.engineConfig.configString调整块大小,但需重启,风险高 - 重建集合:用
db.collection.aggregate([{$out: "new_collection"}])导出存活数据,再renameCollection切换,最后删旧集合——空间立刻释放,且无锁时间仅在重命名瞬间 - 定期
db.collection.reIndex()可清理索引碎片(不影响数据文件),对查询性能有帮助,但不释放磁盘 - 启用 TTL 索引自动过期,比批量删除更平滑,适合日志类场景
执行 compact 前必须检查的三件事
跳过检查容易触发长时间阻塞甚至失败:
- 确认当前是主节点:
db.isMaster().ismaster必须为true;副本集从节点上执行会报错 - 检查磁盘剩余空间:WiredTiger 需要**额外 1.5× 当前
storageSize的空闲空间**来完成重写,否则命令直接失败 - 确认没有正在运行的长事务或 snapshot 操作(如某些备份工具会 hold snapshot),否则
compact会卡住等待
真正难的不是命令怎么敲,而是判断“此刻能不能压这个按钮”——磁盘余量、主从状态、外部工具依赖,任何一个没对齐,compact 就会变成一场值班事故。











