db.collection.remove()后索引大小不降不是bug,而是WiredTiger设计行为:删除仅标记索引页为可复用,不归还OS空间;需执行db.runCommand({compact: "mycol", force: true})强制重建索引结构才能回收空间。

为什么 db.collection.remove() 后索引大小不降
这不是 bug,是 WiredTiger 的设计行为:删除文档只标记索引页为“可复用”,不归还 OS 空间。执行 db.mycol.stats().indexSize 会发现数值几乎不变;ls -lh /data/mongodb/*/mydb/mycol-*.wt 也看不到文件变小。真正占用磁盘的,是那些散落在 .wt 文件里的“空页”。
必须用 compact + force: true 才能回收索引空间
默认的 db.runCommand({compact: "mycol"}) 只保证重写数据文件,索引是否被整理取决于引擎内部策略,不可控。实测在 MongoDB 4.4+ 版本中,必须显式加 force: true 才能强制重建所有索引结构:
db.runCommand({compact: "mycol", force: true})
- 该命令只在 replica set 的
primary上生效;secondary 上运行无效果 - 执行前务必确认
rs.isMaster().ismaster === true - 对复合索引、文本索引、TTL 索引均有效,但 TTL 索引 compact 后建议补一句
db.mycol.reIndex(),避免后台线程残留元数据干扰
执行前不检查这三件事,大概率白忙活
跳过检查,轻则命令失败,重则触发磁盘满或孤儿 chunk 风险:
- 查磁盘余量:
df -h /data/mongodb—— compact 期间会生成新 .wt 文件,临时占用 ≈ 原集合totalSize的 1.2 倍 - 查孤儿 chunk:
db.getSiblingDB("config").chunks.find({ns: "mydb.mycol"}).count()和db.getSiblingDB("config").chunks.find({ns: "mydb.mycol", "shard": {$exists: false}}).count(),后者非零说明有孤儿数据,需先清理 - 查 WiredTiger 可复用空间:
db.mycol.stats().wiredTiger["block-manager"]["file bytes available for reuse"],值越大,碎片越严重,compact 收益越明显
分片集合不能直接 compact,得用 defragmentCollection
对分片集合执行 db.runCommand({compact: ...}) 是无效的——它只作用于本地分片,不协调全局状态。MongoDB 6.0+ 提供专用命令:
sh.adminCommand({ defragmentCollection: "mydb.mycol" })
- 该操作由 balancer 触发,在迁移窗口内执行,避免额外负载冲击
- 合并 chunk 会丢失 placement history,快照读和事务可能短暂失败(通常 5 分钟内恢复)
- 如果影响过大,可用
chunkDefragmentationThrottlingMS参数限速 - MongoDB 7.0+ 已默认自动合并小 chunk,除非监控到 CRUD 延迟明显升高,否则无需手动干预
mongostat 的 lock 情况。


















