在 mongodb 7.0 中,explain("executionstats") 是分析慢查询执行计划的唯一可靠入口;它提供 nreturned、totaldocsexamined、totalkeysexamined 和 executionstages.stage 四个关键字段,真实反映扫描量与索引使用情况,而默认 explain() 仅返回 queryplanner,无法判断实际性能瓶颈。

直接结论:在 MongoDB 7.0 中,explain("executionStats") 是分析慢查询执行计划的**唯一可靠入口**;只用 explain() 或 explain("queryPlanner") 看不到真实扫描量,等于没查。
为什么必须用 explain("executionStats") 而不是默认模式
默认调用 explain() 只返回 queryPlanner,它只告诉你“MongoDB 想怎么执行”,不告诉你“实际扫了多少”。真正决定慢不慢的是三个数字:
-
executionStats.nReturned:最终返回几条 —— 应用看到的结果数 -
executionStats.totalDocsExamined:为凑出这几条,读了多少完整文档 —— 这个数远大于nReturned(比如 50万 vs 200),说明索引没覆盖、大量回表过滤 -
executionStats.totalKeysExamined:读了多少索引项 —— 理想情况下应接近nReturned;如果远高于它,说明索引前缀没对齐等值条件
另外,executionStats.executionStages.stage 必须是 IXSCAN 或 IXSCAN+FETCH,而不是 COLLSCAN;后者代表根本没走索引。
如何对线上慢查询补上 explain("executionStats")
不能只靠开发环境猜。必须从真实慢查询日志或当前运行操作中提取原始语句,再补上 .explain("executionStats"):
- 从 profiler 抓到的慢操作里复制
command字段(如{"find":"orders","filter":{"uid":123,"status":"paid"}}),改写成:db.orders.find({"uid":123,"status":"paid"}).explain("executionStats") - 用
$currentOp找正在卡住的查询:db.getSiblingDB("admin").aggregate([{$currentOp:{}}, {$match:{"secs_running":{$gt:3}}}, {$limit:1}]),取出command后同样补.explain("executionStats") - 注意:聚合查询要写成
db.collection.aggregate([...]).explain("executionStats"),不是.find()
executionStats 里最该盯死的四个字段
别被一堆嵌套结构带偏,只盯这四个位置:
-
executionStats.executionTimeMillis:总耗时。但单独看意义有限——如果缓存命中,它可能虚低;得结合totalDocsExamined判断是否真快 -
executionStats.executionStages.stage === "COLLSCAN":立刻建索引,别犹豫 -
executionStats.executionStages.inputStage.keyPattern:确认命中的索引名是否是你预期的那个。比如你建了{"status":1,"created_at":1},结果这里显示{"status":"hashed"},说明哈希索引被误用了,对范围查询无效 -
executionStats.executionStages.inputStage.isMultiKey === true:说明字段是数组(如tags: ["a","b"]),触发了多键索引,索引膨胀严重,此时即使走了索引,totalDocsExamined也可能异常高,需考虑改用partialFilterExpression限定子集
容易被忽略的陷阱:explain("allPlansExecution") 别乱开
这个模式会**真实执行所有候选执行计划**来比速度,代价极高:
- 可能锁表,尤其在写密集场景下引发连锁超时
- 会放大慢查询本身的副作用(比如触发大量磁盘 IO)
- 只应在低峰期、单次诊断时使用,且必须加
{maxTimeMS: 2000}防死锁 - 日常排查完全不需要它——
executionStats已足够定位 95% 的问题
真正难的从来不是看懂 explain 输出,而是把 totalDocsExamined 和索引字段顺序、查询谓词类型($exists、$in、数组操作)严丝合缝地对应起来。这点错一点,索引就形同虚设。











