判断索引是否生效,核心看explain("executionStats")中nReturned与totalDocsExamined比值:接近则未走索引,远小于则索引用但效果差,相等且值小为理想;再查stage是否为IXSCAN而非COLLSCAN。

选错索引不会直接导致慢查询,但会让 MongoDB 拒绝使用它——真正慢的是 fallback 到 COLLSCAN(全表扫描)。 你建了索引却没生效,比没建还危险:它让你误以为“已优化”,实际每次都在扫全部文档。
为什么 explain("executionStats") 显示走了索引,但还是慢?
常见假象:执行计划里出现 IXSCAN,但 docsExamined 和 nReturned 差距极大(比如 docsExamined=10万,nReturned=1)。这说明索引被用了,但过滤效率极低——只靠前导字段匹配,后续字段没参与筛选。
- 复合索引
{ a: 1, b: 1, c: 1 },查询{ a: 1, c: "x" }只能用上a,c被跳过,剩下 99% 文档靠内存 filter - 分片集群中,索引只建在 mongos 上,没同步到每个 shard 的 primary,
explain汇总结果会掩盖单个 shard 的 COLLSCAN -
$regex开头带^才能走索引;/abc/这种全局匹配必然退化
如何确认当前查询到底用了哪个索引?
别信 getIndexes() 列出来的“有”,要验证它是否真被选中。最可靠方式是强制指定并对比:
- 先跑一次原查询的
explain("queryPlanner"),看winningPlan里的stage和indexName - 再用
hint()强制走你怀疑的索引:db.col.find({a:1,b:2}).hint({a:1,b:1}).explain("executionStats"),对比executionTimeMillis和docsExamined - 如果 hint 后变快,说明查询计划器选错了;如果更慢或报错
bad hint,说明该索引根本无法支撑这个查询结构
重建索引前必须检查的三件事
盲目 dropIndex + createIndex 可能让情况更糟,尤其在线上环境。
- 确认写入是否暂停:用
db.currentOp({secs_running: {$gt: 5}})查长事务,killOp非关键操作,否则前台建索引会锁死整个集合 - 验证索引元数据完整性:运行
db.col.validate({full: true}),若返回valid: false或errors数组非空,先按损坏流程处理,别急着重建 - 检查字段顺序和方向:索引
{ status: 1, createdAt: -1 }和查询{ status: "done", createdAt: { $gt: ISODate(...) } }是匹配的;但反过来{ createdAt: -1, status: 1 }就无法支持该范围查询
真正卡点不在“建不建索引”,而在“索引能否覆盖查询的所有筛选条件+排序字段”。很多慢查修复后仍慢,是因为只补了 where 条件,忘了 sort 或 projection 也需要被索引包含——尤其是分页场景下 sort().skip().limit(),缺一不可。


















