MongoDB中确认索引是否真未被使用,须用$indexStats聚合阶段查accesses.ops字段:连续7天为0且跨完整业务周期无访问,方可判定闲置;但需排除定时任务等低频依赖,并优先hideIndex()预演而非直接删除。

怎么确认一个索引是不是真没被用过
别光看“很久没改过”就删,得用数据说话。MongoDB 从 3.2 开始提供 $indexStats 聚合阶段,它能告诉你每个索引最近被查询命中的次数。执行下面这句就能拿到真实使用统计:
db.user_activities.aggregate([{$indexStats: {}}, {$project: {key: 1, name: 1, accesses: "$accesses.ops"}}])
重点看 accesses.ops 字段:如果连续 7 天都是 0,基本可以判定为闲置;但要注意,有些索引只在报表导出、定时任务或低频后台接口里用,所以建议至少观察一个完整业务周期(比如大促前后)。另外,_id_ 索引永远显示高访问量,别动它。
dropIndex() 和 dropIndexes() 到底该选哪个
想删单个索引,用 dropIndex();想批量清理一批已确认无用的索引,用 dropIndexes() 更高效。但注意两个关键区别:
-
dropIndex("status_1")是精确匹配名称,名称错一个字符就报index not found -
dropIndexes(["status_1", "updated_at_-1"])可以一次清掉多个,但如果列表里某个索引不存在,整个命令会失败,不会跳过 - 直接调
dropIndexes()(不带参数)会删掉除_id_外所有索引,风险极高,生产环境禁止裸用
实操建议:先用 getIndexes() 导出当前索引列表,人工筛选出待删项,再拼成带参数的 dropIndexes([...]) 命令,避免误删。
为什么不能直接删,而要先 hideIndex()
MongoDB 4.2+ 支持 hideIndex(),这是生产环境最稳妥的“预演”方式。隐藏后索引物理还在,但查询优化器完全忽略它——相当于给索引按了静音键。
操作流程是:
- 执行
db.collection.hideIndex("user_id_1") - 观察 24 小时内慢查询告警、应用日志里的
executionStats.nReturned是否骤降 - 如果有关键查询变慢,立刻
db.collection.unhideIndex("user_id_1")恢复
比直接删安全得多,尤其适合不敢停服又怕踩坑的团队。注意:隐藏操作本身不锁表,但切记不要对 _id_ 或唯一约束索引使用 hideIndex(),可能引发写入异常。
分片集群和副本集的删除陷阱
在分片环境下,必须连到 mongos 执行所有索引操作,连错 config server 或 shard 节点会导致命令无效。副本集里删索引虽会通过 oplog 同步,但 secondary 节点重建索引期间可能短暂落后——如果你刚删完就切主,新 primary 上还没同步完,会出现索引状态不一致。
更隐蔽的问题是:某些驱动(比如旧版 PyMongo)在连接字符串里没配 readPreference=primary,删完索引后立刻查,可能路由到还没同步完的 secondary,返回空结果或错误计划。建议删完后手动在各节点上跑一遍 getIndexes() 确认状态一致。
真正麻烦的不是“怎么删”,而是删完才发现某个凌晨三点跑的归档脚本悄悄依赖了那个索引——所以任何索引清理动作,都得留好回滚路径:记录删前的 createIndex() 命令全文,存进配置中心或 CMDB,而不是只靠脑子记。











