MongoDB explain 怎么看?如何判断查询有没有走索引?
简化版
MongoDB 可以用 explain() 查看查询执行计划,重点看 winningPlan、是否出现 IXSCAN、是否有 COLLSCAN、扫描文档数和返回文档数的比例、排序是否使用索引、执行耗时等。走索引不一定就快,关键是索引是否能显著减少扫描并支持排序。
详细版
常用写法:
db.orders.find({ userId: 100 }).explain("executionStats")
重点字段:
winningPlan:最终选择的计划;IXSCAN:索引扫描;COLLSCAN:集合扫描;totalKeysExamined:扫描索引键数量;totalDocsExamined:扫描文档数量;nReturned:返回文档数量;executionTimeMillis:执行耗时;SORT:是否发生额外排序。
如果 totalDocsExamined 远大于 nReturned,通常说明过滤效率不高,需要检查索引、查询条件和数据分布。
完整版教学
一、explain 解决什么问题
查询慢时,不能只靠猜。explain() 能告诉我们 MongoDB 选择了什么执行计划,扫描了多少索引键和文档,是否发生全集合扫描。
例如:
db.users.find({ email: "a@example.com" }).explain("executionStats")
可以判断是否使用了 email 索引。
二、IXSCAN 和 COLLSCAN 怎么看
IXSCAN 表示使用索引扫描,通常是好信号。COLLSCAN 表示集合扫描,可能要扫描大量文档。
但不能机械认为 COLLSCAN 一定错。小集合、低选择性条件、返回大部分数据时,集合扫描可能合理。
真正要看的是:扫描量是否符合业务预期。
三、看扫描数量和返回数量
三个数字很关键:
totalKeysExamined
totalDocsExamined
nReturned
理想情况下,扫描数量和返回数量差距不要太夸张。如果返回 10 条,却扫描 100 万条,说明查询效率很差。
常见原因:
- 缺少合适索引;
- 复合索引字段顺序不匹配;
- 查询条件选择性差;
- 排序没走索引;
- 使用了低效正则或表达式;
- 数组字段导致索引膨胀。
四、SORT 节点可能是性能瓶颈
如果查询需要排序:
db.orders.find({ userId: 100 }).sort({ createdAt: -1 })
最好有类似索引:
db.orders.createIndex({ userId: 1, createdAt: -1 })
否则 MongoDB 可能先取出大量数据再内存排序,数据量大时性能很差。
执行计划中出现显式 SORT 时,要检查排序是否能被索引覆盖。
五、覆盖查询为什么快
如果查询需要的字段都在索引里,MongoDB 可以尽量只读索引而不回表取完整文档,这叫覆盖查询。
例如索引:
db.users.createIndex({ email: 1, name: 1 })
查询:
db.users.find(
{ email: "a@example.com" },
{ _id: 0, email: 1, name: 1 }
)
这种情况下可能减少文档读取。
六、走索引不等于查询一定优秀
面试里要特别说明:IXSCAN 是好信号,但不是最终结论。真正要看索引扫描是否足够收敛,是否还要回表读取大量文档,是否出现额外 SORT,以及返回数量和扫描数量是否匹配。
| 指标 | 理想状态 | 风险信号 |
|---|---|---|
totalKeysExamined | 接近返回量或可接受 | 扫描大量索引键 |
totalDocsExamined | 接近 nReturned | 回表文档远大于返回量 |
nReturned | 符合业务分页 | 返回少但扫描多 |
SORT | 被索引顺序支持 | 内存排序或大量排序 |
比如返回 20 条订单却扫描 200000 个索引键,虽然形式上走了 IXSCAN,也说明索引选择性或复合索引顺序有问题。优化目标是让扫描范围变小,而不是只追求执行计划里出现索引名字。
七、常见误区与追问
好执行计划大致形态:
IXSCAN -> FETCH -> LIMIT
可疑执行计划:
COLLSCAN -> SORT -> LIMIT
IXSCAN 扫描 1000000 keys -> 返回 10 docs
记忆钩子:explain 不只看“有没有 IXSCAN”,还要看“扫了多少、回了多少、排没排序、耗了多久”。
- 误区:出现
IXSCAN就说明查询已经优化好了。 还要看扫描键数、扫描文档数、返回数和是否额外排序,低选择性索引也可能很慢。 - 误区:
COLLSCAN一定不可接受。 小集合、返回大部分数据、低选择性条件下集合扫描可能合理,关键看扫描成本是否符合业务规模。 - 误区:
executionTimeMillis是唯一判断标准。 时间受缓存、并发和机器状态影响,扫描数量和计划结构更能稳定反映问题。 - 追问:
totalDocsExamined远大于nReturned说明什么? 通常说明过滤不够收敛、索引不合适、回表太多或查询条件选择性差。 - 追问:执行计划里看到
SORT怎么办? 检查排序字段能否放入复合索引,让过滤和排序按同一索引顺序完成。 - 追问:覆盖查询为什么能减少成本? 如果过滤和返回字段都在索引中,就可以减少读取完整文档的次数,降低 I/O 和缓存压力。
八、加强记忆
MongoDB explain 看四件事:有没有 IXSCAN,有没有 COLLSCAN,扫描数和返回数差多少,排序是否被索引支持。优化目标不是形式上走索引,而是少扫文档、少排序、少回表。