本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
查询计划分析
通过解释计划进行查询计划分析可提供对 Amazon DocumentDB 查询性能的基本见解。使用 ExecutionStats 的查询计划揭示了关键指标,包括:
-
每个阶段返回的文档(n已返回)
-
Stage-specific 执行时间(执行TimeMillisEstimate)
-
计划生成持续时间(计划TimeMillis)
通过检查查询计划输出,开发人员可以分析执行模式,评估索引利用率,并识别整个查询管道阶段的潜在优化机会。
要分析查询计划,您可以使用以下格式的 explain () 命令。
db.runCommand({explain: {query document}, verbosity: "executionStats"}) db.collection.find().explain("executionStats");
以下是一个操作示例:
db.collection.find({ companyname: { '$eq': 'ANYCOMPANY' }, isDeleted: { '$eq': false } }).sort({"createdAt":1}).limit(2).explain("executionStats");
此操作的输出将类似于以下内容:
{ queryPlanner: { plannerVersion: 2, namespace: 'limit_test.test', winningPlan: { stage: 'LIMIT_SKIP', inputStage: { stage: 'SORT', sortPattern: { createdAt: 1 }, inputStage: { stage: 'IXSCAN', indexName: 'companyname_1_createdAt_1_isDeleted_1', direction: 'forward', indexCond: { '$and': [ { companyname: { '$eq': 'ANYCOMPANY' } }, { isDeleted: { '$eq': false } } ] } } } } }, indexFilterSet: false, indexFilterApplied: false, executionStats: { executionSuccess: true, executionTimeMillis: '4.186', planningTimeMillis: '3.909', executionStages: { stage: 'LIMIT_SKIP', nReturned: '2', executionTimeMillisEstimate: '0.199', inputStage: { stage: 'SORT', nReturned: '2', executionTimeMillisEstimate: '0.197', sortPattern: { createdAt: 1 }, inputStage: { stage: 'IXSCAN', nReturned: '34', executionTimeMillisEstimate: '0.151', indexName: 'companyname_1_createdAt_1_isDeleted_1', direction: 'forward', indexCond: { '$and': [ { companyname: { '$eq': 'ANYCOMPANY' } }, { isDeleted: { '$eq': false } } ] } } } } }, serverInfo: { host: 'demo-cluster', port: 27017, version: '5.0.0' }, ok: 1, operationTime: Timestamp({ t: 1759915116, i: 1 }) }
以下是对 Amazon DocumentDB 查询执行计划的详细分析,详细分析了每个组件及其性能特征。
总体时机
执行TimeMillis 表示查询所花费的总时间,包括计划时间。
planning TimeMillis 表示查询所花费的总计划时间。
执行阶段
它描述了 Amazon DocumentDB 用于执行查询的分步过程,显示了数据如何通过不同的操作流动。
"executionStages": { "stage": "[STAGE_NAME]", "nReturned": "[NUMBER_OF_DOCS]", "executionTimeMillisEstimate": "[TIME]", "inputStage": { // Nested stages } }
查询计划中的常见阶段
以下是查询计划中常见的执行阶段。每个阶段都返回执行TimeMillisEstimate (执行时间)和 nReturn(文档数)指标,以帮助评估每个阶段的查询性能。
COLLSCAN(馆藏扫描)
-
逐个文档扫描整个馆藏文档
-
nReturen:返回集合中所有匹配的文档
-
可能是一项昂贵的操作,在没有可用的索引时可以看到
IXSCAN(索引扫描)
-
使用索引查找匹配的文档
-
nReturen:仅根据索引匹配文档
-
高效运行,比 COLLSCAN 更受青睐
SORT
-
根据指定字段对文档进行排序
-
如果查询中的排序属性不是索引的一部分,则此阶段将显式显示
-
n返回:与输入文档的编号相同
-
Memory-intensive 用于大型结果集。要进行优化,请将排序字段名称添加到索引中
极限_跳过
-
控制返回和跳过的文档数量
-
n返回:受限值限制
-
用于分页或限制操作
子扫描
-
执行嵌套查询操作
-
nReturen:因子查询结果而异
-
用于具有多个阶段的复杂查询
执行力高的阶段TimeMillisEstimate 是优化的理想选择。
注意
ExecutionStats 参数目前不支持更新和删除命令。
在 “解释计划执行统计信息” 中了解已检查的文档
当您使用运行查询时explain("executionStats"),Amazon DocumentDB 会提供检查指标,帮助您了解为生成查询结果而扫描了多少文档。这些指标对于识别低效的查询计划和优化性能非常有用。
注意
仅在亚马逊 DocumentDB 8.0.0+ 版本中可用。
字段
| 字段 | 说明 | 级别 |
|---|---|---|
| 总计 DocsExamined | 在所有执行阶段检查的文档总数。 | Top-level 执行统计 |
| 已检查文档 | 特定执行阶段检查的文件数量。 | Stage-level |
该docsExamined字段出现在以下阶段:
| 暂存区 | 说明 |
|---|---|
| COLLSCAN | 馆藏扫描。馆藏中的所有文件都经过检查。 |
| IXSCAN | 索引扫描。仅检查符合索引条件的文档。 |
| FETCH | 当优化器在与 IXSCAN 不同的阶段检索文档时,FETCH 阶段会报告 docSexamined。在索引扫描计划中,子级 IXSCAN 阶段不报告 docSexamined。在 $lookup 计划中,FETCH 阶段及其子阶段都可能报告 docSexamined。 |
注意
docsExamined不会出现在 IXONLYSCAN 阶段上,因为无需访问文档即可完全从索引中满足查询。
在 “解释计划” 执行统计输出中显示已检查的文档
以下示例使用包含 500,000 个文档的集合,并演示了不同查询执行阶段的docsExamined变化情况。
创建示例数据:
for (let i = 0; i < 500000; i++) { db.coll.insertOne({ number: i, arr: [i, [i+1]], value: "test", bool: i % 2 === 0 }); }
馆藏扫描 (COLLSCAN)
如果没有索引,Amazon DocumentDB 会执行完整的馆藏扫描。
db.coll.find({ "number": { "$lt": 500 } }).explain("executionStats").executionStats
输出:
{ "executionSuccess" : true, "nReturned" : "500", "executionTimeMillis" : "282.055", "planningTimeMillis" : "0.085", "totalDocsExamined" : "500000", "executionStages" : { "stage" : "COLLSCAN", "nReturned" : "500", "executionTimeMillisEstimate" : "281.915", "docsExamined" : "500000", "filter" : { "number" : { "$lt" : 500 } } } }
对所有 500,000 份文件进行了检查,得出了 500 份结果。在数字字段上创建索引可以改善这种情况。
索引扫描 (IXSCAN)
db.coll.createIndex({ number: 1 }); db.coll.find({ "number": { "$lt": 5000 } }).explain("executionStats").executionStats
输出:
{ "executionSuccess" : true, "nReturned" : "5000", "executionTimeMillis" : "3.047", "planningTimeMillis" : "0.296", "totalDocsExamined" : "5000", "executionStages" : { "stage" : "IXSCAN", "nReturned" : "5000", "executionTimeMillisEstimate" : "2.576", "indexName" : "number_1", "direction" : "forward", "docsExamined" : "5000", "indexCond" : { "$and" : [ { "number" : { "$lt" : 5000 } } ] } } }
使用该索引,在50万份文档中仅检查和提取了5,000份,与返回的数字相符。索引条件筛选出了 49.5万份与查询不匹配的文档。
使用残留过滤器进行索引扫描
db.coll.find({ "number": { "$lt": 5000 }, "arr": { "$gt": 4000 } }).explain("executionStats").executionStats
输出:
{ "executionSuccess" : true, "nReturned" : "999", "executionTimeMillis" : "15.367", "planningTimeMillis" : "0.115", "totalDocsExamined" : "5000", "executionStages" : { "stage" : "IXSCAN", "nReturned" : "999", "executionTimeMillisEstimate" : "15.170", "indexName" : "number_1", "direction" : "forward", "docsExamined" : "5000", "indexCond" : { "$and" : [ { "number" : { "$lt" : 5000 } } ] }, "filter" : { "arr" : { "$gt" : 4000 } } } }
索引条件匹配了 500,000 个文档中的 5,000 个,然后启用剩余过滤器将结果arr减少到 999 个。5,000 的docsExamined值反映了在索引条件之后但在应用残差过滤器之前检查的所有文档。
使用 IXSCAN 获取
db.coll.find({ "$or": [{ "number": { "$lt": 100000 } }, { "number": { "$gt": 400000 } }] }).explain("executionStats").executionStats
输出:
{ "executionSuccess" : true, "nReturned" : "199999", "executionTimeMillis" : "899.801", "planningTimeMillis" : "0.183", "totalDocsExamined" : "199999", "executionStages" : { "stage" : "FETCH", "nReturned" : "199999", "executionTimeMillisEstimate" : "894.141", "docsExamined" : "199999", "inputStage" : { "stage" : "IXOR", "nReturned" : "0", "executionTimeMillisEstimate" : "874.897", "inputStages" : [ { "stage" : "IXSCAN", "nReturned" : "100000", "executionTimeMillisEstimate" : "462.208", "indexName" : "number_1", "indexCond" : { "$and" : [ { "number" : { "$lt" : 100000 } } ] } }, { "stage" : "IXSCAN", "nReturned" : "99999", "executionTimeMillisEstimate" : "412.684", "indexName" : "number_1", "indexCond" : { "$and" : [ { "number" : { "$gt" : 400000 } } ] } } ] } } }
当 Amazon DocumentDB 优化器使用提取阶段检索文档时,FETCH 阶段会报告。docsExamined子级 IXSCAN 阶段不报告,docsExamined因为它们只扫描索引键,而不直接访问文档。
使用聚合查询获取
db.coll.explain("executionStats").aggregate([ { $match: { "number": { "$lt": 5 } } }, { $lookup: { from: "coll", pipeline: [{ $match: { "number": { "$lt": 3 } } }], as: "sub" } } ]).executionStats
输出:
{ "executionSuccess" : true, "nReturned" : "5", "executionTimeMillis" : "0.525", "planningTimeMillis" : "0.327", "totalDocsExamined" : "9", "executionStages" : { "stage" : "NESTED_LOOP_LOOKUP", "nReturned" : "5", "executionTimeMillisEstimate" : "0.163", "inputStages" : [ { "stage" : "IXSCAN", "nReturned" : "5", "executionTimeMillisEstimate" : "0.039", "indexName" : "number_1", "direction" : "forward", "docsExamined" : "5", "indexCond" : { "$and" : [ { "number" : { "$lt" : 5 } } ] } }, { "stage" : "FETCH", "nReturned" : "1", "executionTimeMillisEstimate" : "0.009", "docsExamined" : "1", "inputStage" : { "stage" : "AGGREGATE", "nReturned" : "1", "executionTimeMillisEstimate" : "0.044", "inputStage" : { "stage" : "IXSCAN", "nReturned" : "3", "executionTimeMillisEstimate" : "0.013", "indexName" : "number_1", "direction" : "forward", "docsExamined" : "3", "indexCond" : { "$and" : [ { "number" : { "$lt" : 3 } } ] } } } } ] } }
外部 IXSCAN 检查了 5 份与数字 < 5 相匹配的文档。内部 IXSCAN 检查了 3 个与数字 < 3 匹配的文档,FETCH 阶段检查了 1 个汇总结果。9 totalDocsExamined 中的是所有阶段的总和(5 + 3 + 1)。