已废弃的历史多余索引需同时满足:accesses.since早于2025-03-01且accesses.ops为0;排除unique、TTL、partial、hidden等功能性索引;再通过key字段比对识别完全重复索引(但unique重复项必须跳过);最终在从库预演后批量删除。

确认哪些索引属于“已废弃的历史多余索引”
先别急着删,得把真正该删的筛出来——不是所有访问量低的索引都该动。执行 db.collection_name.aggregate([{$indexStats: {}}, {$sort: {"accesses.ops": 1}}]) 查看当前集合索引的累计访问次数和最后访问时间。
【accesses.since 必须早于 2025-03-01(即超18个月未访问)且 accesses.ops === 0 才进入候选池】。重启 mongod 后 $indexStats 计数清零,所以单看 ops=0 不够,必须结合 since 时间戳交叉验证;若 since 是 2026-08-15,哪怕 ops=0 也不能删——它刚被用过。
接着运行 db.collection_name.getIndexes(),逐条检查候选索引的 options 字段:跳过 "unique": true、"expireAfterSeconds"、"partialFilterExpression"、"hidden": true 的条目。这些索引即使不响应查询,也承担约束校验、TTL 清理或灰度评估功能,删了会直接破坏业务逻辑。
识别并标记完全重复索引
打开 mongosh,对目标集合逐个执行以下脚本(替换 coll 为真实集合名):
const coll = "logs_202406"; const indexes = db.getCollection(coll).getIndexes(); const seen = new Set(); indexes.forEach(idx => { if (idx.name === "_id_" || idx.name.startsWith("system.")) return; const keyStr = JSON.stringify(idx.key); if (seen.has(keyStr)) { print(`⚠️ 冗余索引: ${coll} → ${idx.name} (${keyStr})`); } else { seen.add(keyStr); } });
这个脚本只比对 key 字段(如 {"userId": 1, "status": -1}),不校验 background 或 unique 等选项——这是故意的,因为完全重复的索引连字段顺序和方向都一样,留一个就够了。输出里带 ⚠️ 的就是待删对象,记下它的 name 值,比如 "userId_1_status_-1_2"。
注意:如果两个索引 key 相同但一个是 unique: true,脚本也会报冗余,但你必须手动跳过它——【unique 索引绝不可因重复而删除】,否则后续插入重复值会静默失败或抛出违反约束错误。
安全批量删除索引
第一步:在从节点或备份库上预演删除命令,确认无误后再切回主库操作。
第二步:整理待删索引名列表,例如:["userEmail_1", "createdAt_-1", "userId_1_status_-1_2"]。
第三步:执行批量删除:db.collection_name.dropIndexes(["userEmail_1", "createdAt_-1", "userId_1_status_-1_2"])。该命令一次提交,原子生效,返回结果中 nIndexesWas 应等于你传入的数组长度。
第四步:立即验证:db.collection_name.getIndexes() 输出中不应再出现上述索引名。若发现残留,说明某索引名拼写错误或已被其他进程重建,需重新核对。

















