首页 > 数据库 >MongoDB explain命令使用步骤与代码示例

MongoDB explain命令使用步骤与代码示例

来源:互联网 2026-07-25 08:53:02

在实际的数据库调优工作中,explain 命令可以说是最趁手的诊断工具。它能帮你把 MongoDB 查询的执行计划完整地摊开在桌面上,哪一步走了索引、哪一步走了全表扫描、到底花了多少时间,一目了然。下面通过几个具体的场景,把 explain 的用法和输出解读彻底说清楚。 1. 基本使用 先看最基础的

在实际的数据库调优工作中,explain 命令可以说是最趁手的诊断工具。它能帮你把 MongoDB 查询的执行计划完整地摊开在桌面上,哪一步走了索引、哪一步走了全表扫描、到底花了多少时间,一目了然。下面通过几个具体的场景,把 explain 的用法和输出解读彻底说清楚。

MongoDB explain命令使用步骤与代码示例

长期稳定更新的攒劲资源: >>>点此立即查看<<<

1. 基本使用

先看最基础的用法,直接调 explain() 就能拿到当前查询的执行计划,不指定模式的话默认走 queryPlanner

示例:基本 explain 使用

db.students.find({ studentId: 12345 }).explain();

2. explain() 的三种模式

explain 命令支持三种模式,区别在于输出信息的详细程度:

  • queryPlanner:只返回查询的逻辑计划和索引使用情况,不真正执行查询,轻量快捷。
  • executionStats:返回逻辑计划 + 索引使用情况 + 实际执行统计(扫描了多少文档、花了多少毫秒),最常用。
  • allPlansExecution:返回所有备选计划的执行统计,适合对比不同索引策略的性能。

示例:不同模式的 explain

// queryPlanner 模式
db.students.find({ studentId: 12345 }).explain("queryPlanner");

// executionStats 模式(推荐)
db.students.find({ studentId: 12345 }).explain("executionStats");

// allPlansExecution 模式
db.students.find({ studentId: 12345 }).explain("allPlansExecution");

3. explain() 输出解读

拿到输出后,关键看哪几个字段?以下面这个 executionStats 模式的输出为例,逐一拆解。

示例输出(executionStats 模式)

{
  "queryPlanner": {
    "plannerVersion": 1,
    "namespace": "school.students",
    "indexFilterSet": false,
    "parsedQuery": { "studentId": { "$eq": 12345 } },
    "winningPlan": {
      "stage": "FETCH",
      "inputStage": {
        "stage": "IXSCAN",
        "keyPattern": { "studentId": 1 },
        "indexName": "studentId_1",
        "direction": "forward",
        "indexBounds": { "studentId": [ "[12345, 12345]" ] }
      }
    },
    "rejectedPlans": []
  },
  "executionStats": {
    "executionSuccess": true,
    "nReturned": 1,
    "executionTimeMillis": 2,
    "totalKeysExamined": 1,
    "totalDocsExamined": 1,
    "executionStages": {
      "stage": "FETCH",
      "nReturned": 1,
      "executionTimeMillisEstimate": 0,
      "works": 2,
      "advanced": 1,
      "needTime": 0,
      "needYield": 0,
      "sa veState": 0,
      "restoreState": 0,
      "isEOF": 1,
      "invalidates": 0,
      "docsExamined": 1,
      "alreadyHasObj": 0,
      "inputStage": {
        "stage": "IXSCAN",
        "nReturned": 1,
        "executionTimeMillisEstimate": 0,
        "works": 2,
        "advanced": 1,
        "needTime": 0,
        "needYield": 0,
        "sa veState": 0,
        "restoreState": 0,
        "isEOF": 1,
        "invalidates": 0,
        "keyPattern": { "studentId": 1 },
        "indexName": "studentId_1",
        "isMultiKey": false,
        "multiKeyPaths": { "studentId": [] },
        "indexBounds": { "studentId": [ "[12345, 12345]" ] },
        "keysExamined": 1,
        "seeks": 1,
        "dupsTested": 0,
        "dupsDropped": 0
      }
    }
  },
  "serverInfo": {
    "host": "localhost",
    "port": 27017,
    "version": "4.4.6",
    "gitVersion": "22c124145fa3bfdaeafb3f6d1b5f3d4e8391fe86"
  }
}

关键字段解读

  • queryPlanner 部分:
    • namespace:数据库.集合名,一眼知道查的是哪张表。
    • parsedQuery:MongoDB 解析后的查询条件,用来确认查询是否被正确转换。
    • winningPlan:最终选中的执行计划,里面会暴露索引使用情况(比如这里用了 studentId_1 索引做 IXSCAN,然后 FETCH 回表取文档)。
    • rejectedPlans:被优化器抛弃的备选计划,如果这里不为空,说明存在多个候选索引,值得关注。
  • executionStats 部分:
    • executionSuccess:查询是否正常执行完。
    • nReturned:最终返回了多少条文档。
    • executionTimeMillis:总执行时间(毫秒),优化前后可以直接对比这个数值。
    • totalKeysExamined:扫描的索引键总数。理想情况下应该等于 nReturned,如果远大于 nReturned,说明索引选择性不好。
    • totalDocsExamined:扫描的文档总数。越小越好,如果这个数很大而 nReturned 很小,很可能没有走索引或者索引不能覆盖查询。
  • executionStages 部分(执行计划的树形结构):
    • stage:每个步骤的名称,常见的包括 IXSCAN(索引扫描)、FETCH(回表取文档)、COLLSCAN(全表扫描,见它基本可以断定索引没用好)。
    • nReturned:该步骤返回的文档数。
    • executionTimeMillisEstimate:该步骤的估计耗时。
    • keysExamineddocsExamined:该步骤扫描的索引键数和文档数,逐层累加。
    • inputStage:下一级输入(子步骤),层层嵌套直到叶子节点。

4. 示例:复合索引和多条件查询

实际业务中往往不是单字段查询,比如要按姓氏 + 名字精确查找。假设已经建了一个复合索引 { lastName: 1, firstName: 1 },看看 explain 怎么反馈。

创建复合索引

db.students.createIndex({ lastName: 1, firstName: 1 });

查询及执行计划分析

db.students.find({ lastName: "Smith", firstName: "John" }).explain("executionStats");

示例输出及解读

{
  "queryPlanner": {
    "plannerVersion": 1,
    "namespace": "school.students",
    "indexFilterSet": false,
    "parsedQuery": { "lastName": { "$eq": "Smith" }, "firstName": { "$eq": "John" } },
    "winningPlan": {
      "stage": "FETCH",
      "inputStage": {
        "stage": "IXSCAN",
        "keyPattern": { "lastName": 1, "firstName": 1 },
        "indexName": "lastName_1_firstName_1",
        "direction": "forward",
        "indexBounds": {
          "lastName": [ "["Smith", "Smith"]" ],
          "firstName": [ "["John", "John"]" ]
        }
      }
    },
    "rejectedPlans": []
  },
  "executionStats": {
    "executionSuccess": true,
    "nReturned": 1,
    "executionTimeMillis": 1,
    "totalKeysExamined": 1,
    "totalDocsExamined": 1,
    "executionStages": {
      "stage": "FETCH",
      "nReturned": 1,
      "executionTimeMillisEstimate": 0,
      "works": 2,
      "advanced": 1,
      "needTime": 0,
      "needYield": 0,
      "sa veState": 0,
      "restoreState": 0,
      "isEOF": 1,
      "invalidates": 0,
      "docsExamined": 1,
      "alreadyHasObj": 0,
      "inputStage": {
        "stage": "IXSCAN",
        "nReturned": 1,
        "executionTimeMillisEstimate": 0,
        "works": 2,
        "advanced": 1,
        "needTime": 0,
        "needYield": 0,
        "sa veState": 0,
        "restoreState": 0,
        "isEOF": 1,
        "invalidates": 0,
        "keyPattern": { "lastName": 1, "firstName": 1 },
        "indexName": "lastName_1_firstName_1",
        "isMultiKey": false,
        "multiKeyPaths": { "lastName": [], "firstName": [] },
        "indexBounds": {
          "lastName": [ "["Smith", "Smith"]" ],
          "firstName": [ "["John", "John"]" ]
        },
        "keysExamined": 1,
        "seeks": 1,
        "dupsTested": 0,
        "dupsDropped": 0
      }
    }
  }
}

从输出可以看到,复合索引被完美利用:先按 lastName 精确匹配,再按 firstName 精确匹配,totalKeysExaminedtotalDocsExamined 都只扫了 1 条,效率极高。如果 totalDocsExamined 远大于 nReturned,通常说明索引没有覆盖查询条件,或者查询字段顺序与索引字段顺序不匹配。

用好 explain,就等于给查询做了一次“X光检查”。每次建完索引、调完查询后,顺手跑一遍 explain("executionStats"),重点看 totalKeysExaminedtotalDocsExamined 这两个数字——数字越小,数据库就越轻松。坚持这个习惯,查询性能的提升会非常直观。

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

热游推荐

更多
湘ICP备2026025700号-3 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。