应查db.collectionName.stats()中storageSize是否超size2倍以上,确认索引与数据严重膨胀;再结合慢日志、currentOp、getIndexes分析索引实际使用情况,避免误删。

索引物理大小远超数据逻辑大小,怎么确认?
别只看 db.stats() 的总空间,重点查单个集合的碎片率:db.collectionName.stats() 返回结果里,如果 storageSize 比 size 大 2 倍以上,说明索引(和数据)已严重膨胀。WiredTiger 引擎下,索引文件(index-*.wt)和数据文件(collection-*.wt)混存在同一目录,但 storageSize 高通常意味着索引写入频繁、删除不规律,或建了大量低选择性字段的索引。
删掉不用的索引前,先验证它真没被用
直接 db.collectionName.dropIndex() 很快,但可能误伤线上查询。必须先查实际使用情况:
- 启用慢日志并跑 24 小时:在
mongod.conf中设operationProfiling.mode: slowOp+operationProfiling.slowOpThresholdMs: 100 - 用
db.currentOp({secs_running: {$gt: 0.1}})抓实时长查询,看queryPlanner.winningPlan里是否命中目标索引 - 检查
db.collectionName.getIndexes()输出中每个索引的key字段,排除仅含_id或全为text的冗余索引(尤其旧版本遗留的)
冷热数据分离时,索引要按数据走,不能一刀切
把历史数据迁到归档库后,原集合索引不会自动瘦身——WiredTiger 不会回收已分配的页。正确做法是:
- 对原集合执行
db.collectionName.createIndex({ts: 1}, {background: true})(假设ts是时间字段),确保新写入热数据能走新索引 - 归档完成后,用
db.collectionName.dropIndex({ts: 1})删除旧复合索引中包含历史字段的部分(如{status: 1, ts: 1}) - 对归档库单独建精简索引,例如只保留
{_id: 1}和极少数查询字段,避免复制原库全部索引
注意:副本集里做这事,务必选在从节点维护窗口期操作,否则 compact 或重建索引会锁表并触发全量同步。
compact 命令不是万能的,它有硬性前提
db.runCommand({compact: "collectionName"}) 只能在以下条件同时满足时才真正释放磁盘空间:
- 集合所在数据库未开启 journal(或 journal 已刷盘完成)
- 执行时无写入流量(否则 compact 会失败并报
BackgroundOperationInProgressForNamespace) - 空闲空间 > 110% 的
storageSize(官方文档明确写的阈值)
更现实的做法是:先停写,用 mongodump 导出,删库重建,再 mongorestore ——虽然耗时,但能彻底清除碎片,且对副本集主节点也安全(只要在 secondary 上操作并提前降级)。


















