MongoDB删除数据后磁盘空间不释放是WiredTiger引擎正常行为,compact命令必须直连各shard主节点单独执行,mongos不转发该命令,且需确认引擎为WiredTiger、碎片率低于0.7、磁盘预留至少2GB临时空间。

删除数据后磁盘空间不释放,不是 MongoDB “坏了”,而是 WiredTiger 引擎的默认行为:它把删掉的空间标记为“可重用”,但不会自动归还给操作系统。在分片集群里,这个问题更隐蔽——因为 compact 命令根本不能在 mongos 上执行,误操作只会静默失败或报错。
为什么连 mongos 执行 compact 会失败
分片集群中,mongos 是路由层,不存储数据;真正存数据的是各个 shard 节点(即独立的 mongod 实例)。compact 是存储引擎级命令,必须直连到具体 shard 的主节点(或副本集成员)才能生效。
常见错误现象:
- 在
mongos上执行db.runCommand({compact: "mycol"}),返回"command not supported from mongos" - 无报错但日志里没 compact 记录,
df -h磁盘使用率纹丝不动 - 误以为“已执行”,结果所有 shard 都没处理,空洞持续累积
compact 前必须确认的三件事
盲目运行 compact 可能导致 shard IO 打满、查询延迟飙升,甚至因磁盘空间不足而中断。它不是“一键瘦身”,而是重写整个集合文件 + 所有索引。
-
db.serverStatus().storageEngine.name必须返回"wiredTiger"—— MMAPv1 已废弃,且compact对其不释放磁盘空间 -
db.mycol.stats().size / db.mycol.stats().storageSize比值低于0.7才值得压缩;否则碎片少,收益极低 -
df -h查 shard 数据目录所在磁盘:WiredTiger 要求至少预留2GB临时空间(重写期间需双倍空间)
如何正确对每个 shard 执行 compact
不能只连一个节点就停手。每个 shard 是独立数据单元,且 compact 不同步、不复制,必须逐个登录、逐个执行、逐个验证。
- 先用
sh.status()查目标 collection 分布在哪几个 shard 上(如shard0001、shard0002) - 用
mongo --host <shard_host>:port -u <user> -p <pass>直连该 shard 主节点(不要连 mongos) - 执行:
use your_db_name→db.runCommand({compact: "your_collection", force: true})(MongoDB 4.2+ 主节点上force: true是必需的) - 查日志:
grep "compact" /var/log/mongodb/mongod.log,等出现"Compact completed"再切下一个 shard - 严禁并发执行多个
compact—— 单 shard 的 IO 会被彻底打满,拖垮整个分片的服务能力
compact 成功了,但 df 还是没变小?
这是最常被忽略的一点:compact 完成后,WiredTiger 确实释放了内部空间,但操作系统层面的文件尺寸不一定立刻收缩。Linux 下 .wt 文件可能仍占用原大小,直到内核完成实际回收(受 fsync、page cache、文件系统策略影响)。
更关键的是:这个过程不可控,也不保证立即可见。如果你发现 df 几小时后仍无变化,别反复跑 compact —— 它不会叠加效果,反而增加风险。此时应检查是否真有足够空闲空间触发回收,或者考虑更彻底的方案:secondary 节点重同步(适用于副本集部署的 shard)。

















