判断索引是否生效,核心看executionstats.nreturned与totaldocsexamined比值:接近则未走索引,远小于则索引用但效果差,相等且值小为理想;再查stage是否为ixscan而非collscan。

explain 返回的 executionStats 里看什么指标?
索引是否生效,核心就看 executionStats.nReturned 和 executionStats.totalDocsExamined 的比值。如果两者接近(比如相差不到 10%),大概率没走索引——MongoDB 扫了几乎所有文档才凑出结果;如果 nReturned 远小于 totalDocsExamined(比如返回 10 条但扫了 10 万条),说明索引用了但效果差(比如只用上复合索引前缀);理想情况是两者相等且 totalDocsExamined 很小。
还要盯住 executionStats.executionStages.stage:出现 COLLSCAN 就是全表扫描,索引彻底失效;IXSCAN 表示走了索引,但得继续往下看它是否被高效利用。
为什么 explain("executionStats") 比 explain("queryPlanner") 更有用?
queryPlanner 只告诉你“MongoDB 认为该用哪个索引”,是预估;executionStats 是真实执行后的数据,包含实际扫描文档数、返回数、执行耗时,能验证预估是否靠谱。
常见误判场景:
- 查询条件含
$or,但各分支没共用字段,MongoDB 可能弃用索引改 COLLSCAN - 对索引字段做了函数操作,如
{ $gt: { $add: ["$ts", 3600] } },索引无法下推 - 使用
$regex且模式以通配符开头(^缺失),如/abc/,B-tree 索引失效
复合索引字段顺序不对,explain 会怎么暴露?
查 executionStats.executionStages.indexBounds,它显示实际用于查找的索引范围。如果复合索引是 { a: 1, b: 1, c: 1 },但查询只用了 b 和 c,你会看到 indexBounds 中 a 的范围是 [MinKey, MaxKey] —— 这意味着索引第一字段未被约束,MongoDB 实际只能靠 b,c 做范围扫描,效率大打折扣。
更隐蔽的问题:
-
sort字段不在索引中,或顺序与索引不一致,会导致额外内存排序(executionStats.executionStages.stage === "SORT") -
limit很小但totalDocsExamined很大,说明索引没覆盖sort或projection,MongoDB 不得不取出大量文档再裁剪
聚合管道里索引容易被忽略的几个点
聚合阶段顺序直接影响索引能否生效:$match 和 $sort 必须尽可能前置,且字段需在索引中。一旦中间插入 $project、$addFields 或 $unwind,后续 $match 就无法利用原始集合索引。
实操检查项:
- 运行
db.collection.explain("executionStats").aggregate([...]),确认第一个$match阶段对应IXSCAN - 如果管道含
$lookup,被联查集合上的过滤条件必须写在pipeline内部,且字段要有对应索引,否则外层索引无效 -
$text查询必须搭配text索引,且不能和其他非$text查询条件混用在同一个$match中,否则索引失效
真正难排查的是那些“看似走了索引,但 totalDocsExamined 异常高”的情况——这往往意味着索引设计和查询模式之间存在隐性错配,得一层层拆开 executionStages 树去看每个子阶段的 stage 和 nReturned。











