explain 方法详细文档
📑 目录
- 概述
- 核心特性
- 使用场景
- 方法签名
- 参数说明
- verbosity 模式
- 使用示例
- 性能优化建议
- 聚合管道的 explain
- monSQLize explain 与原生 MongoDB 的对比
- 注意事项
- 错误处理
- 常见问题
- 参考资料
概述
explain 方法用于分析查询执行计划,帮助诊断性能问题和优化查询策略。直接使用 MongoDB 原生 Cursor.explain() 方法,返回查询执行计划而非实际数据,专用于性能诊断。
核心特性
- ✅ 原生 MongoDB API:直接调用
cursor.explain()方法 - ✅ 3 种详细级别:
queryPlanner(默认)/executionStats/allPlansExecution - ✅ 完整查询支持:filter、projection、sort、limit、skip、hint、collation 等所有原生选项
- ✅ 多操作支持:find、aggregate、count、distinct 等查询操作
- ✅ 性能分析:索引使用情况、扫描文档数、执行时间等详细统计
使用场景
1. 验证索引使用
检查查询是否使用了预期的索引,识别全表扫描(COLLSCAN)问题。
2. 诊断慢查询
分析查询瓶颈(全表扫描、内存排序、多阶段处理等),找出性能优化点。
3. 对比查询策略
比较不同 hint/query 的性能差异,选择最优索引和查询条件。
4. 优化复杂查询
分析聚合管道、多字段查询等复杂查询的执行计划,优化管道顺序和索引设计。
方法签名
monSQLize 提供两种方式使用 explain,与原生 MongoDB 完全兼容:
方式 1:链式调用(与原生 MongoDB 一致)
优点:
- ✅ 与原生 MongoDB API 完全一致
- ✅ 语法简洁直观
- ✅ 适合快速性能分析
注意:
- 链式调用时不能使用 sort、limit 等选项
- 如需这些选项,请使用方式 2
方式 2:options 参数(支持完整查询选项)
优点:
- ✅ 支持 sort、limit、skip、projection、hint 等所有查询选项
- ✅ 支持所有查询方法(find、aggregate、count、distinct)
- ✅ 参数集中,代码清晰
参数说明
verbosity 参数
指定返回的详细级别,决定执行计划包含的信息量。
查询选项(所有 find/aggregate 支持的选项)
核心选项(MongoDB 原生支持):
聚合管道选项(aggregate 操作):
verbosity 模式
1. queryPlanner(默认)
返回查询优化器选择的执行计划,不执行查询。最轻量,适合快速检查索引使用情况。
返回信息:
queryPlanner.winningPlan: 查询优化器选择的计划queryPlanner.rejectedPlans: 被拒绝的候选计划queryPlanner.parsedQuery: 解析后的查询条件
2. executionStats
实际执行查询并返回详细统计信息(扫描文档数、耗时等)。适合性能分析。
返回信息:
executionStats.executionTimeMillis: 执行耗时(毫秒)executionStats.totalDocsExamined: 扫描的文档数executionStats.totalKeysExamined: 扫描的索引键数executionStats.nReturned: 返回的文档数executionStats.executionStages: 详细的执行阶段信息
3. allPlansExecution
返回所有候选执行计划及其试执行结果。适合理解优化器的选择过程。
返回信息:
- 包含
queryPlanner和executionStats的所有信息 executionStats.allPlansExecution: 所有候选计划的执行详情
使用示例
示例 1: 基本查询计划分析
示例 2: 执行统计分析
示例 3: 索引优化分析
示例 4: hint 强制索引选择
示例 5: 所有候选计划分析
示例 6: 慢查询诊断
性能优化建议
1. 识别全表扫描
2. 检查索引覆盖
3. 分析排序性能
聚合管道的 explain
聚合管道 explain 示例
聚合管道优化建议
monSQLize explain 与原生 MongoDB 的对比
原生 MongoDB explain(链式调用)
monSQLize explain(完全兼容)
方式 1:链式调用(与原生一致)
注意:链式调用时,sort/limit/skip 等选项需要在 find 的第二个参数中指定:
方式 2:options 参数(推荐,功能更完整)
实现原理
monSQLize 通过在 Promise 对象上添加 explain() 方法来实现链式调用:
这样既可以:
- 直接
await collection('products').find({ ... })获取查询结果 - 也可以
await collection('products').find({ ... }).explain('executionStats')获取执行计划
核心原则
- ✅ 完全兼容:支持原生 MongoDB 的链式调用语法
- ✅ 返回值一致:返回完全相同的执行计划对象
- ✅ 向后兼容:仍然支持 options 参数方式
- ✅ 支持所有操作:find、aggregate、count、distinct 等都支持 explain 选项
注意事项
- explain 不返回实际数据:仅返回执行计划和统计信息,不返回查询结果
- executionStats 会执行查询:
executionStats和allPlansExecution模式会实际执行查询以收集统计信息 - 生产环境谨慎使用:在生产环境使用
executionStats可能影响性能,建议在低峰期或测试环境使用 - hint 谨慎使用:强制指定索引可能绕过优化器的智能选择,使用前应通过 explain 验证效果
- verbosity 参数:
queryPlanner:不执行查询,开销最小executionStats:执行查询,返回统计信息allPlansExecution:执行所有候选计划,开销最大
- 与缓存的关系:explain 查询不会触发 monSQLize 的缓存机制
- 慢查询日志:当使用
executionStats且执行时间超过配置的慢查询阈值时,会记录到慢查询日志
错误处理
常见问题
Q1: explain 的 verbosity 应该选择哪个?
选择建议:
- 快速检查索引使用:使用
queryPlanner(默认),不执行查询,开销最小 - 分析实际性能:使用
executionStats,获取执行时间、扫描文档数等实际统计 - 对比多个索引方案:使用
allPlansExecution,查看所有候选计划的性能
Q2: explain 会影响数据库性能吗?
影响程度:
queryPlanner:无影响,仅分析查询计划,不执行查询executionStats:有一定影响,需要实际执行查询收集统计allPlansExecution:影响较大,需要试执行所有候选计划
建议:
- 开发/测试环境:可随意使用
- 生产环境:优先使用
queryPlanner,需要executionStats时选择低峰期
Q3: 如何理解 explain 返回的执行计划?
关键字段:
stage: 'COLLSCAN':全表扫描(性能差)stage: 'IXSCAN':索引扫描(性能好)stage: 'FETCH':根据索引获取完整文档stage: 'SORT':内存排序(可优化)stage: 'PROJECTION_COVERED':覆盖索引(最优)
性能指标:
totalDocsExamined:扫描的文档数(越少越好)totalKeysExamined:扫描的索引键数nReturned:返回的文档数executionTimeMillis:执行时间(毫秒)- 查询效率 =
nReturned / totalDocsExamined(接近 100% 最好)
Q4: 为什么我创建了索引,explain 还是显示 COLLSCAN?
可能原因:
- 查询条件不匹配索引:索引是
{ name: 1 },但查询条件是{ email: 'xxx' } - 数据量太小:集合文档数少于 100 时,优化器可能选择全表扫描
- 索引选择性差:查询条件匹配的文档数超过集合的 30%,优化器认为全表扫描更快
- 索引未生效:索引正在构建中(
db.currentOp()查看)
解决方法:
Q5: 聚合管道应该如何优化?
优化原则:
- $match 前置:尽早过滤数据,减少后续阶段的处理量
- $project 延后:在需要时才投影字段,避免传递不必要的数据
- 利用索引:$match、$sort 等阶段尽量使用索引
- 避免 $lookup 大集合:联表查询开销大,考虑数据冗余设计
示例: