该explain()命令演示如何Azure DocumentDB 执行查询。 用于 explain() 确定查询是否使用索引、扫描的文档数以及出现性能瓶颈的位置。
运行说明
在任何find或aggregate命令中,将explain()附加到查询后面即可运行。
db.collection.find({ field: "value" }).explain("executionStats")
使用三种详细程度模式中的一种:
| Mode | 说明 |
|---|---|
"queryPlanner" |
返回最优查询计划,而不运行该查询。 |
"executionStats" |
运行查询并返回获胜计划的执行统计信息。 |
"allPlansExecution" |
运行查询并返回所有候选计划的统计信息。 |
对于性能分析,请使用 "executionStats" 作为起点。
了解输出结构
解释输出分为两个主要部分。
queryPlanner
本 queryPlanner 部分介绍查询引擎在运行查询之前如何计划执行查询。
"queryPlanner": {
"winningPlan": {
"stage": "FETCH",
"inputStage": {
"stage": "IXSCAN",
"indexFilterSet": [
{ "$eq": { "status": "1" } }
],
"indexName": "status_1"
}
}
}
queryPlanner 中的关键字段:
| 领域 | 说明 |
|---|---|
winningPlan |
优化器选择的查询计划。 |
winningPlan.stage |
执行阶段。 常见值为COLLSCAN和 IXSCANFETCH。 |
winningPlan.inputStage |
向当前阶段输入数据的子阶段。 |
rejectedPlans |
优化器未选择的其他候选计划。 |
executionStats
该 executionStats 部分包含实际查询执行中的指标。
"executionStats": {
"nReturned": 10,
"totalKeysExamined": 10,
"totalDocsExamined": 10,
"executionTimeMillis": 3
}
executionStats 中的关键字段:
| 领域 | 说明 |
|---|---|
nReturned |
返回给客户端的文档数。 |
totalKeysExamined |
扫描的索引条目数。 |
totalDocsExamined |
从集合中扫描的文档数。 |
executionTimeMillis |
执行查询的总时间(以毫秒为单位)。 |
确定执行阶段
每个 winningPlan 阶段表示查询管道中的操作。 识别各个阶段,以了解数据如何流经执行计划。
| 阶段 | 说明 |
|---|---|
COLLSCAN |
完整集合扫描。 引擎读取所有文档。 如果不存在合适的索引,则会出现此阶段。 |
IXSCAN |
索引扫描。 引擎读取索引条目以查找匹配的文档。 |
FETCH |
文档提取。 引擎在扫描索引后从存储加载完整文档。 |
SORT |
内存排序 如果没有索引支持排序顺序,则会出现此阶段。 |
COUNT_SCAN |
使用索引计数。 此阶段可避免加载文档以满足计数操作。 |
COUNT |
内存计数。 引擎在从集合中加载文档后统计文档数量。 |
GROUP |
内存中分组操作,通常由 $group 聚合阶段生成。 |
LIMIT |
限制传递到下一阶段的文档数。 |
DISTINCT_SCAN |
扫描索引以返回字段的非重复值,跳过重复索引条目。 |
DISTINCT_UNWIND |
在非重复扫描期间展开数组值,以单独枚举每个元素。 |
UNIQUE |
重复数据删除输入阶段中的文档,用于不同的操作。 |
PARALLEL_MERGE |
将多个并行执行分支的结果合并到单个流中。 |
检测性能问题
使用以下模式确定解释输出中的常见性能问题。
集合扫描而不是索引扫描
阶段 COLLSCAN 意味着查询读取集合中的每个文档。 对于大型集合,此操作成本高昂。
指标:winningPlan.stage 是 "COLLSCAN".
"winningPlan": {
"stage": "COLLSCAN"
}
行动: 在查询筛选器中使用的字段上创建索引。 如果查询筛选多个字段、对结果进行排序或使用聚合阶段(例如 $group ,或 $lookup)创建涵盖所有相关字段的复合索引。
// Single field
db.collection.createIndex({ field: 1 })
// Multiple fields (filter + sort)
db.collection.createIndex({ filterField: 1, sortField: 1 })
// Aggregation pipeline with $match on multiple fields
db.collection.createIndex({ status: 1, createdAt: -1 })
检查的文档与返回的文档的比率高
比较totalDocsExamined与nReturned。 很大的差异意味着查询会扫描许多文档,但返回的次数很少。
指标:totalDocsExamined 明显高于 nReturned。
"nReturned": 5,
"totalDocsExamined": 50000
行动: 添加更具选择性的索引或优化查询筛选器以减少扫描范围。
内存排序
在 FETCH 或 COLLSCAN 阶段之后的 SORT 阶段表示排序操作会在加载文档后于内存中执行。 内存中排序会消耗内存并大规模降低查询速度。
指标: 计划中会显示一个阶段 SORT 。
"winningPlan": {
"stage": "SORT",
"inputStage": {
"stage": "COLLSCAN"
}
}
行动: 创建包含排序字段的索引,并遵循复合索引的相等、排序、范围(ESR)规则。
db.collection.createIndex({ filterField: 1, sortField: 1 })
执行时间缓慢
高 executionTimeMillis 值表示查询速度缓慢。
指标:executionTimeMillis 返回的文档数高于预期。
"executionTimeMillis": 8500,
"nReturned": 20
操作:检查是否存在COLLSCAN、高totalDocsExamined或内存中SORT阶段,并使用适当的索引解决这些问题。
解读经过良好优化的查询语句
经过良好优化的查询会使用索引,仅扫描必要的键,并在无需进行内存排序的情况下返回文档。
"executionStats": {
"nReturned": 10,
"totalKeysExamined": 10,
"totalDocsExamined": 10,
"executionTimeMillis": 2,
"winningPlan": {
"stage": "FETCH",
"inputStage": {
"stage": "IXSCAN",
"indexName": "status_1_createdAt_1"
}
}
}
在本示例中:
-
totalKeysExamined等于nReturned,这意味着该索引具有选择性。 -
totalDocsExamined等于nReturned,这意味着不会获取额外的文档。 - 该执行计划先使用
IXSCAN,后接FETCH,这符合索引查询的预期模式。
聚合管道的运行说明
在聚合管道上运行 explain() 以查看每个阶段的执行计划。
db.collection.explain("executionStats").aggregate([
{ $match: { status: "active" } },
{ $sort: { createdAt: -1 } },
{ $limit: 10 }
])
输出包括一个 stages 数组,其中显示了每个管道阶段如何映射到内部执行计划。 检查第一阶段中是否有 COLLSCAN 或 IXSCAN,以确认 $match 筛选器是否使用索引。