MongoDBNotes

第 10 章:执行计划

zjc 于 2026-01-10 发布

这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 执行计划解释 MongoDB 如何执行查询:使用哪个索引、扫描多少文档、是否内存排序、返回多少结果。优化查询不能靠猜,应以 explain 输出为准。

10.1 基本用法

查询计划:

db.orders.find({
  user_id: "u_1001",
  status: "PAID"
}).explain()

详细执行统计:

db.orders.find({
  user_id: "u_1001",
  status: "PAID"
}).explain("executionStats")

所有计划输出:

db.orders.find({
  user_id: "u_1001"
}).explain("allPlansExecution")

聚合计划:

db.orders.aggregate([
  { $match: { user_id: "u_1001" } },
  { $sort: { created_at: -1 } }
]).explain()

10.2 关键字段

常见字段:

字段 含义
stage 执行阶段
winningPlan 优化器选择的计划
rejectedPlans 被拒绝计划
indexName 使用索引
keysExamined 索引条目扫描数
docsExamined 文档扫描数
nReturned 返回数
totalKeysExamined 总索引扫描
totalDocsExamined 总文档扫描
executionTimeMillis 执行时间

10.3 常见阶段

阶段 说明
COLLSCAN 集合扫描
IXSCAN 索引扫描
FETCH 回表取文档
PROJECTION_COVERED 覆盖索引投影
SORT 内存排序
SORT_KEY_GENERATOR 排序键生成
LIMIT 限制条数
SUBPLAN $or 子计划
GEO_NEAR 地理距离

COLLSCAN 不一定是错误,小集合或后台任务可以接受;高频在线查询通常必须避免。

10.4 读取计划

示例:

winningPlan
  FETCH
    IXSCAN idx_user_time_status

判断:

  1. 是否使用预期索引;
  2. keysExamined 是否接近 nReturned
  3. docsExamined 是否远大于 nReturned
  4. 是否有 SORT
  5. 是否被覆盖索引覆盖;
  6. 执行时间是否集中在某个阶段。

10.5 扫描效率

理想比例:

nReturned ≈ keysExamined

示例:

keysExamined docsExamined nReturned 判断
100 100 100 高效
100000 100000 100 选择性差
100 100000 100 过滤后置
0 1000000 0 集合扫描

索引选择性差时,调整字段顺序或查询条件比强制提示更可靠。

10.6 排序计划

无索引排序:

SORT
  FETCH
    IXSCAN idx_user

有索引排序:

FETCH
  IXSCAN idx_user_created

排序阶段可能触发内存限制。分页和列表查询应尽量让排序字段进入复合索引。

10.7 覆盖查询

集合:

db.users.insertOne({
  _id: "u_1001",
  name: "Alice",
  level: "GOLD"
})

索引:

db.users.createIndex({ user_id: 1, status: 1, created_at: -1 })

覆盖查询需要查询字段和投影字段都在索引中。覆盖查询可以减少 FETCH,但不要为了覆盖所有场景建立过多索引。

10.8 查询计划缓存

MongoDB 会缓存查询计划,数据分布变化后可能重新评估。查看:

db.orders.getPlanCache().list()

清理:

db.orders.getPlanCache().clear()

不要把清理缓存当成常规优化。应优先修正索引和查询。

10.9 索引提示

强制使用索引:

db.orders.find({
  user_id: "u_1001",
  status: "PAID"
}).hint("idx_user_time_status")

提示可以验证索引收益,但长期使用会让计划失去适应性。索引结构变化后,提示可能导致查询失败或退化。

10.10 生产分析流程

find slow query
  -> read explain
  -> identify scan / sort stage
  -> inspect indexes
  -> design new index
  -> verify executionStats
  -> deploy during low peak
  -> monitor latency and write cost

上线新索引时要注意后台构建策略、副本集影响和磁盘空间。

10.11 常见计划问题

现象 可能原因
COLLSCAN 无索引或条件不满足前缀
大量 docsExamined 索引选择性差
SORT 排序字段不在索引
rejectedPlans 多 索引重复或冲突
计划变化 数据分布或缓存变化
聚合慢 $match 后置或数据放大

本章小结

执行计划把查询性能问题变成可观察的证据。重点看 winningPlan、扫描数量、返回数量和排序阶段。查询优化应先减少输入数据,再优化索引顺序和排序,最后考虑缓存与提示。

思考题

  1. COLLSCAN 一定有问题吗?
  2. keysExaminednReturned 的关系说明什么?
  3. 为什么 SORT 阶段需要关注内存?
  4. 覆盖索引的代价是什么?
  5. 为什么不建议长期使用 hint