删除大量数据后索引空间不释放是WiredTiger正常行为,compact命令通过重写数据和索引文件物理整理空间,需在primary执行并确保磁盘余量充足、无孤儿chunk,force:true可提升索引回收效果。

删除大量数据后索引空间不释放,不是 bug,是 WiredTiger 的正常行为:它只标记索引页为“可复用”,不主动归还 OS。单纯 db.collection.remove({}) 或 deleteMany() 后,db.collection.stats() 里的 indexSize 和 totalSize 基本不变,df -h 看不到磁盘下降——这说明索引碎片已堆积。
为什么 compact 能回收索引空间
WiredTiger 的 compact 命令会重写整个集合的数据文件和索引文件(.wt),把散落的、被标记为“可复用”的页重新打包成连续块,并丢弃真正空闲的页。它不是清空缓存,而是物理重组织——所以索引大小会真实下降。
- 必须在
primary上执行;secondary上运行无效 - 对带复合索引、文本索引或 TTL 索引的集合同样生效
- 执行后,
wiredTiger.block-manager.file bytes available for reuse会显著减少,indexSize可能下降 20%–60%,取决于原碎片程度 - 注意:
compact不压缩文档内容(那是compression配置干的事),只整理空间布局
执行 compact 前必须检查的三件事
跳过这些检查,大概率失败或白忙活:
- 确认当前连接的是 replica set 的
primary:运行rs.isMaster().ismaster,返回true才行 - 查
fs.chunks和fs.files是否有孤儿数据:如果只删了fs.files文档但没清理对应fs.chunks,compact完全扫不到那些 chunk,空间照占 - 检查磁盘余量:
compact期间会生成新.wt文件,临时占用 ≈ 原集合totalSize的 1.2 倍;用df -h /data/mongodb确认够不够
compact 索引空间的实际操作顺序
别直接 db.runCommand({compact: "mycol"}) —— 这个命令默认只处理数据文件,索引文件是否被重写取决于引擎内部策略,不可控。要确保索引空间也被回收,必须显式触发完整重写:
- 先停写(或切到维护窗口),避免 compact 中途被写入打断
- 运行
db.runCommand({compact: "mycol", force: true})——force: true强制重建所有索引结构,不是默认行为,但实测在 4.4+ 版本中对索引回收效果更稳定 - 执行后立刻查
db.mycol.stats().indexSize,对比 compact 前数值;再看系统级ls -lh /data/mongodb/*/mydb/mycol-*.wt,文件大小应明显收缩 - 如果集合有 TTL 索引,compact 后建议补一句
db.mycol.reIndex(),防止 TTL 后台线程残留的元数据干扰后续扫描
compact 失效时的替代路径
当 compact 执行完,indexSize 几乎没变,或者 bytes available for reuse 仍高得离谱(比如 >50% totalSize),说明碎片太深或文件已严重膨胀,compact 效率趋近于零:
- 放弃 compact,改走
mongodump+drop+mongorestore:这是唯一能彻底重排索引 B-tree 深度的方法 - dump 时加
--excludeCollection=system.*避免导出系统索引元数据冲突 - restore 前务必
db.mycol.drop()(不是remove({})),否则 restore 会追加而非覆盖 - 该流程需额外 ≥ 当前数据库总大小的磁盘空间,且无法在线完成——最容易被忽略的是:很多人 dump 后忘了删旧集合,restore 完两个集合并存,空间反而翻倍
真正卡住人的从来不是命令怎么写,而是执行前没确认 primary 身份、没清理孤儿 chunk、没预留够临时空间——这三步错一步,compact 就等于没做。


















