首页/数据/NoSQL 速查/隐藏索引做上线前验证
MongoDB99 条

隐藏索引做上线前验证

hideIndex 让索引对查询规划器不可见:可以在不删索引的前提下验证“移除它会影响哪些查询”,随时可恢复。

NoSQL 速查数据更新于 2026-10-13

是什么

隐藏索引(hidden index)是 MongoDB 4.4 引入的上线前验证手段:hideIndex 之后索引仍然存在、仍然随写入维护,但对查询规划器不可见。于是可以先把它「假装删掉」,观察线上查询是否出现全表扫描、性能是否退化,确认无用再真正 dropIndex,确认有用随时 unhideIndex 恢复。它把「删索引」这个不可逆动作拆成了可回退的两步。

基本信息

隐藏db.collection.hideIndex("idx_name") 或 hideIndex({ status: 1 })
恢复db.collection.unhideIndex("idx_name")
创建即隐藏createIndex({...}, { hidden: true })
版本要求MongoDB 4.4 及以上
硬限制_id 索引不能隐藏
查询状态db.collection.getIndexes() 中 hidden 字段为 true

典型使用场景

  • 索引下线前验证:先隐藏,观察若干天慢查询与扫描量,确认无查询依赖再删除
  • 新索引上线前对照:建好后先保持隐藏,用 hint 或 explain 对比,避免新索引抢占计划
  • 容量评估:隐藏冗余索引并观察一段时间,为最终清理提供数据支撑
  • 写入压力排查:确认某些索引只服务于已下线的报表功能,隐藏后写放大是否缓解可以量化
  • 变更窗口操作:需要短时间改变查询计划时,用隐藏代替删除,回退成本最低

隐藏与验证

隐藏与验证
db.orders.getIndexes().map(i => ({ name: i.name, hidden: i.hidden === true }))  # 先列出索引与隐藏状态
db.orders.hideIndex("status_ts")           # 让规划器不再选择该索引
db.orders.find({ status: "paid" }).explain("executionStats")  # 看 winningPlan 是否退化为 COLLSCAN
db.orders.find({ status: "paid" }).hint({ status: 1 })        # 对照实验:强制走另一个索引
db.orders.unhideIndex("status_ts")          # 观察期内发现问题,立刻恢复
db.orders.dropIndex("status_ts")            # 确认长期无用后再删除

隐藏后的观察项

观察对象正常表现异常表现
explain 的 winningPlan仍走其他可用索引或保持索引扫描出现 COLLSCAN
totalDocsExamined与隐藏前量级相当成倍甚至数量级上升
慢查询日志数量无明显变化新增大量慢查询
写入延迟略有下降(索引维护仍在,只是不被选中)不要期待明显改善
隐藏期间索引依然随写入维护、依然占用磁盘,所以隐藏不是省资源的动作,而是零风险的验证动作;真正释放写入开销只能靠 dropIndex。
在副本集与分片集群上隐藏索引是配置变更,需要主节点与多数成员确认;生产环境操作前先确认副本集状态健康,避免在选举或维护窗口内叠加配置变更。

易错点

  • 隐藏后未观察就立即删除:隐藏的价值在于留下观察期,跳过这步等于直接删索引,可能让关键查询退化为全表扫描
  • 隐藏后出现大量 COLLSCAN 却继续推进删除:这正说明该索引仍被线上查询依赖,应先恢复,再针对具体查询设计新的索引
  • 误以为隐藏能立刻降低写入压力:索引仍随写入维护,写放大不会因为隐藏而消失
  • 试图隐藏 _id 索引:这是硬限制,会被直接拒绝,需要调整方案

数据来源与参考

  • MongoDB Manual: Hidden Indexes
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌