前面两篇讲了索引类型和 ESR 原则,但所有这些设计到底有没有生效,只有一个验证手段:explain。问题是,很多人用 explain 的方式是「跑一下,看有没有 IXSCAN 字样」,看完就走了。这种用法只用了 explain 一成的能力,错过了它真正值钱的部分——查询到底扫了多少、回表了多少、慢在哪。
explain 不是一次性命令,它有三个详细程度,每一层回答不同的问题。这一篇讲清楚怎么分层读 explain,以及哪几个指标是判断查询健康的硬指标。
先把机制边界说清楚
explain 有三个详细级别,对应三个问题:
- queryPlanner(默认):这个查询会怎么执行?看优化器选了哪个计划,但不真正执行。
- executionStats:这个查询实际代价多大?真正执行一次,返回扫描量、回表量、耗时。
- allPlansExecution:优化器为什么选这个计划?把所有候选计划都跑一遍,对比代价。
排查慢查询时,几乎总是要从 executionStats 开始——只有它告诉你查询真的扫了多少。queryPlanner 只告诉你「计划长什么样」,但计划可能因为统计信息不准而选错,实际代价和计划设想不一致。
explain 三档
三层各有用途:queryPlanner 最快、用于快速确认走没走索引;executionStats 是主力、给出真实代价;allPlansExecution 用于诊断「为什么优化器选了个慢计划」。
必看的几个指标
executionStats 里指标很多,但真正决定查询健康的是这几个:
nReturned:最终返回了多少文档。这是查询的目的,也是衡量其他指标的基准。
totalKeysExamined:扫描了多少索引键。理想情况下它应该接近 nReturned——扫多少返回多少。如果它远大于 nReturned(比如扫了 10 万键返回 100 条),说明索引选择性差,大量扫到的键最后没匹配。
totalDocsExamined:回表读了多少文档。这个指标最关键:
- 等于 0 → 覆盖查询,索引里就有全部需要的字段,没回表,最快。
- 接近
nReturned→ 健康,几乎每个回表的文档都被返回。 - 远大于
nReturned→ 低效,回表读了大量文档却只返回了少量,索引没起到过滤作用。
executionTimeMillis:总耗时。但更有用的是看它怎么分布在各个 stage——是 IXSCAN 慢、FETCH 慢、还是 SORT 慢。
一个记忆口诀:健康查询的比例是 nReturned : totalKeysExamined : totalDocsExamined ≈ 1 : 1 : 1。任何一项远大于 nReturned,都是优化空间。
怎么判断查询健康
把指标翻译成判断:
看到 COLLSCAN:查询走了全表扫描,没有可用索引。这是第一个红灯——除了极小集合,生产环境的查询不应该出现 COLLSCAN。要么加索引,要么检查查询条件是不是写错了。
看到 SORT stage:排序没走索引,要在内存里排。小结果集没事,大结果集会撞 32MB 上限报错 QueryExceededMemoryLimitNoDiskUseAllowed。解决方法是把排序字段纳入复合索引(按 ESR),让索引天然有序。
totalDocsExamined ≫ nReturned:回表读了大量文档却只返回了少量。要么是索引选择性差(字段值太集中),要么是索引字段顺序不对(按 ESR 调整),要么是查询条件本身就不该走这个索引。
FETCH 占了大头耗时:说明索引定位没问题,但回表读取成了瓶颈。可能是数据不在 Cache(冷数据),也可能是返回字段太多没法覆盖查询。前者是内存问题,后者是索引设计问题。
allPlansExecution:诊断选错计划
有时你会遇到诡异的情况:明明有合适的索引,优化器却选了另一个慢计划。这时要用 allPlansExecution——它让所有候选计划都跑一段(trial period),记录每个的实际代价,你能看到「为什么优化器没选你以为该选的那个」。
优化器选错计划的常见原因:统计信息过时(collStats 里的 cardinality 不准)、数据分布不均(某些值特别集中)、查询条件复杂导致成本估算偏差。5.0 之后 MongoDB 引入了 plan stability 和更稳定的优化器,减少了这类抖动,但仍可能在数据分布特殊时发生。
遇到选错计划,临时的解法是 hint() 强制指定索引,但根本解法是理解为什么优化器估错了,必要时调整索引设计让正确计划明显更优。
一个实际的排查流程
拿到一条慢查询,按这个顺序读 explain:
- 先
explain("executionStats")看走没走索引(IXSCAN vs COLLSCAN)。 - 看
totalKeysExamined和totalDocsExamined与nReturned的比例,判断索引利用效率。 - 看有没有
SORTstage,判断排序是不是瓶颈。 - 如果索引没选对,用
allPlansExecution对比候选计划。 - 根据诊断结果:缺索引就建、顺序不对就按 ESR 调、回表多就考虑覆盖查询、排序慢就把排序字段纳入索引。
判断框架
- 排查慢查询第一步永远是
explain("executionStats"),不是猜。 - 三档各有用途:queryPlanner 看计划、executionStats 看代价、allPlansExecution 看选型。
- 健康比例
1:1:1:返回数 ≈ 扫键数 ≈ 回表数。 COLLSCAN、SORT、totalDocsExamined ≫ nReturned是三大红灯。- 选错计划用
allPlansExecution诊断,临时hint(),根本靠索引设计。 - explain 是索引优化的「测谎仪」——索引建得好不好,explain 说实话,不要凭感觉。
下一篇会沿着 explain 揭示的一个理想状态——totalDocsExamined = 0 的覆盖查询展开,讲清楚怎么让查询完全不回表。
关于十三Tech
我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。
我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。
如果你想继续跟完这套「图解 MongoDB」,欢迎关注公众号 「十三Tech」。后续会按索引优化、存储引擎、高可用和分片集群这条线更新。

