用 db.collection.getIndexes() 快速识别完全重复索引的方法是遍历所有集合,对每个索引的 key 字段序列化后用 Set 去重,若重复则输出冗余警告;该脚本跳过 _id_ 和系统索引,仅比对字段名、顺序及排序方向,不校验 unique 等选项。

怎么用 db.collection.getIndexes() 快速识别完全重复索引
完全重复索引是危害最大、也最容易发现的一类冗余——两个索引的字段名、顺序、排序方向(1 或 -1)完全一致。MongoDB 不会阻止你创建它们,但写入时会双倍更新、内存中双份缓存。
手动比对效率低还容易漏,直接在 shell 里跑这段逻辑就行:
db.getCollectionNames().forEach(coll => {
const indexes = db.getCollection(coll).getIndexes();
const seen = new Set();
indexes.forEach(idx => {
// 跳过 _id 索引和系统索引
if (idx.name === '_id_' || idx.name.startsWith('system.')) return;
const keyStr = JSON.stringify(idx.key);
if (seen.has(keyStr)) {
print(`⚠️ 冗余索引发现: ${coll} → ${idx.name} (${JSON.stringify(idx.key)})`);
} else {
seen.add(keyStr);
}
});
});
注意点:
- 这个脚本只比对
key字段,不校验background、unique等选项——如果两个索引 key 完全一样但一个是unique: true,那不能删,否则会破坏数据约束 - 输出里的
idx.name是你要传给db.collection.dropIndex()的名字,别手抖删错 - 生产环境执行前,先在从节点或备份库上验证一遍
字段子集索引怎么判断哪个能删
当存在 { userId: 1 } 和 { userId: 1, createdAt: -1 } 时,前者大概率冗余——因为后者能覆盖所有前者的查询场景(比如 { userId: "abc" } 或 { userId: "abc", createdAt: { $gt: ... } })。
但不能无脑删,得看实际查询模式:
- 如果业务里真有大量只查
userId且不要排序的轻量查询(比如用户状态检查),保留短索引反而更省内存、更快定位 - 如果集合文档很大(比如单文档 > 50KB),长索引的 B-tree 层级可能更高,短索引的查找深度反而更优
- 用
db.collection.explain("executionStats").find({ userId: "abc" })对比两者的executionStats.nReturned和executionStats.executionTimeMillis,再决定
简单判断口诀:**子集索引能删,前提是它没被任何高频、低开销查询独占使用。**
用 mongostat 和 db.currentOp() 验证索引是否真“僵尸”
所谓“僵尸索引”,是指创建后从未被查询计划选中过的索引。它不报错、不告警,但持续吃内存、拖慢写入。
光看 getIndexes() 没用,得结合运行时数据:
- 开启慢查询日志(
slowms: 100)并跑 24 小时,再用db.setProfilingLevel(2)抓全量查询,之后分析db.system.profile.find({ "executionStats.nReturned": { $gt: 0 } }).sort({ "ts": -1 }).limit(1000),看哪些索引名没出现在executionStats.executionStages.indexName里 - 用
mongostat --host xxx --port 27017 -o 1000观察idx miss和idx hit比值,长期idx miss > idx hit的索引值得怀疑 -
db.currentOp({ "secs_running": { $gt: 5 }, "secs_running": { $lt: 300 } })查正在卡住的长事务,有时会暴露因锁索引导致的阻塞链,顺藤摸瓜找到冷门索引
自动化清理脚本必须绕开的四个坑
自动删索引不是“批量 dropIndex()”就完事。线上出事往往就栽在这几步:
- 唯一索引(
unique: true)和 TTL 索引(expireAfterSeconds)绝不能进自动清理名单——删了会导致插入重复或过期数据不自动删 - 分片集群里,
dropIndex()必须在 mongos 上执行,且要确认该索引在所有 shard 上都存在;否则部分分片丢失索引,后续路由可能出错 - 不要在一个连接里连续执行多个
dropIndex(),WiredTiger 会锁整个 collection 的元数据;建议每删一个 sleep 100ms,加try/catch并记录失败项 - 删之前必须
mongodump --db mydb --collection mycoll --query '{createdAt: {$gte: ...}}'备份最近 7 天数据——索引删错还能靠时间点恢复,数据误删就是事故
最常被忽略的是:索引删除后,WiredTiger 缓存不会立刻释放内存,得等后台线程回收,观察 db.serverStatus().mem 里的 mapped 和 resident 变化至少要等 5 分钟才真实。


















