View a markdown version of this page

查询计划分析 - Amazon DocumentDB

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

查询计划分析

通过解释计划进行查询计划分析可提供对 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)。