MongoDB仅IXSCAN无FETCH阶段的查询为何为PROJECTION_DEFAULT
仅含IXSCAN无FETCH阶段但标记为PROJECTION_DEFAULT的场景说明
问题描述
需要明确:在哪种场景下,仅包含IXSCAN阶段、无FETCH阶段的查询不会被判定为PROJECTION_COVERED查询,而是被判定为PROJECTION_DEFAULT查询。
现有两组结构相似的聚合查询执行统计数据:一组执行阶段标记为PROJECTION_COVERED,另一组则标记为PROJECTION_DEFAULT。两个查询所需的全部字段均已纳入对应索引,且执行时均正确命中了目标索引。两组查询均为仅包含匹配、分组操作的聚合管道,所有访问字段均被索引覆盖,两组查询的完整执行统计信息如下。
PROJECTION_COVERED 执行统计
"executionSuccess" : true, "nReturned" : 524400, "executionTimeMillis" : 2097, "totalKeysExamined" : 524401, "totalDocsExamined" : 0, "executionStages" : { "stage" : "PROJECTION_COVERED", "nReturned" : 524400, "executionTimeMillisEstimate" : 231, "works" : 524401, "advanced" : 524400, "needTime" : 0, "needYield" : 0, "saveState" : 587, "restoreState" : 587, "isEOF" : 1, "transformBy" : { "cessValue" : 1, "cgstValue" : 1, "differentialTaxRate" : 1, "igstValue" : 1, "placeOfSupply" : 1, "sgstValue" : 1, "supplyTypeType" : 1, "totalTaxRate" : 1, "_id" : 0 }, "inputStage" : { "stage" : "IXSCAN", "nReturned" : 524400, "executionTimeMillisEstimate" : 181, "works" : 524401, "advanced" : 524400, "needTime" : 0, "needYield" : 0, "saveState" : 587, "restoreState" : 587, "isEOF" : 1, "keyPattern" : { "sellerGstin" : 1.0, "returnPeriod" : 1.0, "documentType" : 1.0, "placeOfSupply" : 1.0, "supplyTypeType" : 1.0, "totalTaxRate" : 1.0, "differentialTaxRate" : 1.0, "cgstValue" : 1.0, "sgstValue" : 1.0, "igstValue" : 1.0, "cessValue" : 1.0, "taxableValue" : 1.0 }, "indexName" : "line_item_index_for_b2cs", "isMultiKey" : false, "multiKeyPaths" : { "sellerGstin" : [], "returnPeriod" : [], "documentType" : [], "placeOfSupply" : [], "supplyTypeType" : [], "totalTaxRate" : [], "differentialTaxRate" : [], "cgstValue" : [], "sgstValue" : [], "igstValue" : [], "cessValue" : [], "taxableValue" : [] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "sellerGstin" : [ "[\"92L4YI1LN7TQF2EA\", \"92L4YI1LN7TQF2EA\"]" ], "returnPeriod" : [ "[\"122021\", \"122021\"]" ], "documentType" : [ "[\"CREDIT\", \"CREDIT\"]", "[\"DEBIT\", \"DEBIT\"]", "[\"INVOICE\", \"INVOICE\"]" ], "placeOfSupply" : [ "[MinKey, MaxKey]" ], "supplyTypeType" : [ "[MinKey, MaxKey]" ], "totalTaxRate" : [ "[MinKey, MaxKey]" ], "differentialTaxRate" : [ "[MinKey, MaxKey]" ], "cgstValue" : [ "[MinKey, MaxKey]" ], "sgstValue" : [ "[MinKey, MaxKey]" ], "igstValue" : [ "[MinKey, MaxKey]" ], "cessValue" : [ "[MinKey, MaxKey]" ], "taxableValue" : [ "[MinKey, MaxKey]" ] }, "keysExamined" : 524401, "seeks" : 1, "dupsTested" : 0, "dupsDropped" : 0 } } }
PROJECTION_DEFAULT 执行统计
"executionSuccess" : true, "nReturned" : 126, "executionTimeMillis" : 9, "totalKeysExamined" : 134, "totalDocsExamined" : 0, "executionStages" : { "stage" : "PROJECTION_DEFAULT", "nReturned" : 126, "executionTimeMillisEstimate" : 0, "works" : 134, "advanced" : 126, "needTime" : 7, "needYield" : 0, "saveState" : 1, "restoreState" : 1, "isEOF" : 1, "transformBy" : { "assessableAmount" : 1, "cessAmount" : 1, "cgstAmount" : 1, "documentDetails.documentType" : 1, "gstRate" : 1, "hsnCode" : 1, "igstAmount" : 1, "quantity" : 1, "sgstAmount" : 1, "totalAmount" : 1, "unit" : 1, "_id" : 0 }, "inputStage" : { "stage" : "IXSCAN", "nReturned" : 126, "executionTimeMillisEstimate" : 0, "works" : 134, "advanced" : 126, "needTime" : 7, "needYield" : 0, "saveState" : 1, "restoreState" : 1, "isEOF" : 1, "keyPattern" : { "gstinOrgId" : 1.0, "invoiceMetadata.returnPeriod" : 1.0, "isDeleted" : 1.0, "documentGstStatus" : 1.0, "status" : 1.0, "invoiceMetadata.sectionName" : 1.0, "documentDetails.documentType" : 1.0, "hsnCode" : 1.0, "unit" : 1.0, "gstRate" : 1.0, "totalAmount" : 1.0, "assessableAmount" : 1.0, "cgstAmount" : 1.0, "sgstAmount" : 1.0, "igstAmount" : 1.0, "cessAmount" : 1.0, "quantity" : 1.0 }, "indexName" : "covered-index-for-hsn-autocompute", "isMultiKey" : false, "multiKeyPaths" : { "gstinOrgId" : [], "invoiceMetadata.returnPeriod" : [], "isDeleted" : [], "documentGstStatus" : [], "status" : [], "invoiceMetadata.sectionName" : [], "documentDetails.documentType" : [], "hsnCode" : [], "unit" : [], "gstRate" : [], "totalAmount" : [], "assessableAmount" : [], "cgstAmount" : [], "sgstAmount" : [], "igstAmount" : [], "cessAmount" : [], "quantity" : [] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "gstinOrgId" : [ "[\"8ca21c57-3cd1-4a1a-9aa3-d9bc668ebd9b\", \"8ca21c57-3cd1-4a1a-9aa3-d9bc668ebd9b\"]" ], "invoiceMetadata.returnPeriod" : [ "[\"072022\", \"072022\"]" ], "isDeleted" : [ "[false, false]" ], "documentGstStatus" : [ "[\"FILED\", \"FILED\"]", "[\"FILING_FAILED\", \"FILING_FAILED\"]", "[\"NA\", \"NA\"]", "[\"RESET_FAILED\", \"RESET_FAILED\"]", "[\"STAGED\", \"STAGED\"]", "[\"SUBMISSION_FAILED\", \"SUBMISSION_FAILED\"]", "[\"SUBMITTED\", \"SUBMITTED\"]", "[\"SUBMITTED_FOR_FILING\", \"SUBMITTED_FOR_FILING\"]", "[\"SUBMITTED_FOR_RESET\", \"SUBMITTED_FOR_RESET\"]", "[\"SUBMITTED_FOR_SUBMISSION\", \"SUBMITTED_FOR_SUBMISSION\"]", "[\"SUBMITTED_FOR_SYNC\", \"SUBMITTED_FOR_SYNC\"]", "[\"SUBMITTED_FOR_UPLOAD\", \"SUBMITTED_FOR_UPLOAD\"]", "[\"SYNCED\", \"SYNCED\"]", "[\"SYNC_FAILED\", \"SYNC_FAILED\"]", "[\"UPLOADED\", \"UPLOADED\"]", "[\"UPLOAD_FAILED\", \"UPLOAD_FAILED\"]" ], "status" : [ "[\"GENERATED\", \"GENERATED\"]", "[\"GENERATION_FAILED\", \"GENERATION_FAILED\"]", "[\"IN_PROGRESS\", \"IN_PROGRESS\"]", "[\"NOT_GENERATED\", \"NOT_GENERATED\"]" ], "invoiceMetadata.sectionName" : [ "[\"AT\", \"AT\"]", "[\"B2B\", \"B2B\"]", "[\"B2CL\", \"B2CL\"]", "[\"B2CS\", \"B2CS\"]", "[\"CDNR\", \"CDNR\"]", "[\"CDNUR\", \"CDNUR\"]", "[\"EXP\", \"EXP\"]", "[\"NIL_RATED\", \"NIL_RATED\"]" ], "documentDetails.documentType" : [ "[\"CRN\", \"CRN\"]", "[\"DBN\", \"DBN\"]", "[\"INV\", \"INV\"]" ], "hsnCode" : [ "[MinKey, MaxKey]" ], "unit" : [ "[MinKey, MaxKey]" ], "gstRate" : [ "[MinKey, MaxKey]" ], "totalAmount" : [ "[MinKey, MaxKey]" ], "assessableAmount" : [ "[MinKey, MaxKey]" ], "cgstAmount" : [ "[MinKey, MaxKey]" ], "sgstAmount" : [ "[MinKey, MaxKey]" ], "igstAmount" : [ "[MinKey, MaxKey]" ], "cessAmount" : [ "[MinKey, MaxKey]" ], "quantity" : [ "[MinKey, MaxKey]" ] }, "keysExamined" : 134, "seeks" : 8, "dupsTested" : 0, "dupsDropped" : 0 } }, "allPlansExecution" : [] }
核心结论
首先要明确两个概念的差异:
- 只要执行计划无
FETCH阶段、totalDocsExamined = 0,就说明查询已经是覆盖索引查询,不需要回表读取集合原始文档,性能处于最优区间。 PROJECTION_COVERED是投影阶段的特化快速实现标记,仅当投影逻辑满足严格限制条件时才会触发,不满足条件时哪怕是覆盖索引查询,也会走通用投影逻辑,标记为PROJECTION_DEFAULT。
常见触发场景
- 投影包含点路径写法的嵌套字段引用
这是本次两个案例出现差异的直接原因。PROJECTION_COVERED的快速路径仅支持纯顶层字段的简单包含/排除投影,实现逻辑是直接从索引键中抽取对应顶层字段值拼接输出文档,不支持嵌套结构重组。第二个案例的投影规则包含documentDetails.documentType: 1,需要把索引中扁平化存储的键值重新组装成嵌套结构输出,无法走COVERED特化路径,因此被标记为DEFAULT;第一个案例的投影全是顶层字段,无点路径引用,满足快速路径要求,因此标记为COVERED。 - 投影包含非裁剪类逻辑
只要投影不是简单的字段保留/排除,涉及字段重命名、表达式计算、数组操作、类型转换等逻辑,哪怕所有依赖字段都在索引中,也不会触发COVERED路径。 - 投影上游存在其他处理阶段
如果投影阶段的输入不是直接来自IXSCAN的原始索引键流,中间经过了分组、排序、过滤等其他加工阶段,哪怕这些阶段也被下推、不需要回表,投影阶段也无法使用直接读取索引键的特化逻辑。 - 命中特殊类型索引
如果查询命中多键索引、全文索引、地理空间索引等特殊索引,出于数组、特殊结构解析的正确性考虑,投影阶段不会标记为COVERED。
注意:
PROJECTION_DEFAULT标记不代表查询没有用到覆盖索引,只要无回表操作,性能和PROJECTION_COVERED场景没有本质差异,只是投影阶段走了通用处理逻辑而已。
内容的提问来源于stack exchange,提问作者sprksh
相关产品推荐
相关产品推荐

