当 INDEX 匹配时,MongoDB 阶段是不是应该避免 FETCH?

Posted

技术标签:

【中文标题】当 INDEX 匹配时,MongoDB 阶段是不是应该避免 FETCH?【英文标题】:Does MongoDB stages should avoid FETCH when INDEX is matching?当 INDEX 匹配时,MongoDB 阶段是否应该避免 FETCH? 【发布时间】:2020-02-21 04:38:47 【问题描述】:

根据文档,当索引覆盖查询时,MongoDB 应该跳过 FETCH 阶段。

如果我理解正确,这句话解释了这种行为:

覆盖查询当索引覆盖查询时,MongoDB 可以同时匹配 查询条件并仅使用索引键返回结果; 即 MongoDB 不需要检查集合中的文档以 返回结果。

当索引覆盖查询时,解释结果有一个 IXSCAN 阶段 这不是 FETCH 阶段的后代,并且在 executionStats 中, totalDocsExamined 为 0。

在早期版本的 MongoDB 中,cursor.explain() 返回 indexOnly 字段指示索引是否覆盖查询。 (https://docs.mongodb.com/manual/reference/explain-results/)

还有这个

有了这个,查询花费不到 2 毫秒。因为索引 “覆盖”查询,MongoDB 能够匹配查询条件 并仅使用索引键返回结果;甚至不需要 检查集合中的文档以返回结果。 (如果 您会在 执行计划然后索引“覆盖”了查询。)(https://studio3t.com/knowledge-base/articles/mongodb-index-strategy/)

但在测试场景中它不会发生:

测试示例:

    db.Test.insert("Field1":"data on field1: 1","Field2":"data on field2: 1");
    db.Test.insert("Field1":"data on field1: 2","Field2":"data on field2: 2");
    db.Test.insert("Field1":"data on field1: 3","Field2":"data on field2: 3");
    db.Test.insert("Field1":"data on field1: 4","Field2":"data on field2: 4");
    db.Test.insert("Field1":"data on field1: 5","Field2":"data on field2: 5");
    db.Test.insert("Field1":"data on field1: 6","Field2":"data on field2: 6");

在我为 Field2 创建索引之后。

db.Test.createIndex("Field2":1)

然后我查询集合:

db.Test.find("Field2":"data on field2: 5").explain("executionStats");

我期待一个不是FETCH 阶段的子阶段IDXSCAN。但是输出是这样的:

[...]
"winningPlan" : 
            "stage" : "FETCH",
            "inputStage" : 
                "stage" : "IXSCAN",
                "keyPattern" : 
                    "Campo2" : 1.0
                ,
                "indexName" : "Field2_1",
                "isMultiKey" : false,
                "multiKeyPaths" : 
                    "Campo2" : []
                ,
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : 
                    "Field2" : [ 
                        "[\"data on field2: 5", \"data on field2: 5\"]"
                    ]
                
            
        ,
[...]

有两个阶段:一个 "stage" : "FETCH", 及其子 "stage" : "IXSCAN",

谁能解释我的误解?

*** 关于投影

使用投影运行查询时

"winningPlan" : 
            "stage" : "PROJECTION",
            "transformBy" : 
                "Campo2" : 1.0
            ,
            "inputStage" : 
                "stage" : "FETCH",
                "inputStage" : 
                    "stage" : "IXSCAN",
                    "keyPattern" : 
                        "Field2" : 1.0
                    ,
                    "indexName" : "Field2_1",
                    "isMultiKey" : false,
                    "multiKeyPaths" : 
                        "Campo2" : []
                    ,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 2,
                    "direction" : "forward",
                    "indexBounds" : 
                        "Field2" : [ 
                            "[\"data on field2: 5", \"data on field2: 5\"]"
                        ]
                    
                
            
        ,

亚当的回答是:成功了!

我意识到投影不应包含“_id”以避免FETCH

【问题讨论】:

【参考方案1】:

您的查询没有指定projection,这意味着它将返回文档中的所有字段。这意味着 Field2: 1 索引不包含查询,因为它只包含一个字段。

以下查询应该被完全覆盖并且不应该有一个 FETCH 阶段。请注意,投影明确排除了_id 字段,如will be included in the projection unless specified otherwise:

    db.Test.find(
      "Field2":"data on field2: 5",
      "Field2" : 1, "_id" : 0 
    ).explain("executionStats");

输出:


    "queryPlanner" : 
        "plannerVersion" : 1,
        "namespace" : "foo.Test",
        "indexFilterSet" : false,
        "parsedQuery" : 
            "Field2" : 
                "$eq" : "data on field2: 5"
            
        ,
        "winningPlan" : 
            "stage" : "PROJECTION",
            "transformBy" : 
                "Field2" : 1,
                "_id" : 0
            ,
            "inputStage" : 
                "stage" : "IXSCAN",
                "keyPattern" : 
                    "Field2" : 1
                ,
                "indexName" : "Field2_1",
                "isMultiKey" : false,
                "multiKeyPaths" : 
                    "Field2" : [ ]
                ,
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : 
                    "Field2" : [
                        "[\"data on field2: 5\", \"data on field2: 5\"]"
                    ]
                
            
        ,
        "rejectedPlans" : [ ]
    ,
    "executionStats" : 
        "executionSuccess" : true,
        "nReturned" : 1,
        "executionTimeMillis" : 1,
        "totalKeysExamined" : 1,
        "totalDocsExamined" : 0,
        "executionStages" : 
            "stage" : "PROJECTION",
            "nReturned" : 1,
            "executionTimeMillisEstimate" : 0,
            "works" : 2,
            "advanced" : 1,
            "needTime" : 0,
            "needYield" : 0,
            "saveState" : 0,
            "restoreState" : 0,
            "isEOF" : 1,
            "invalidates" : 0,
            "transformBy" : 
                "Field2" : 1,
                "_id" : 0
            ,
            "inputStage" : 
                "stage" : "IXSCAN",
                "nReturned" : 1,
                "executionTimeMillisEstimate" : 0,
                "works" : 2,
                "advanced" : 1,
                "needTime" : 0,
                "needYield" : 0,
                "saveState" : 0,
                "restoreState" : 0,
                "isEOF" : 1,
                "invalidates" : 0,
                "keyPattern" : 
                    "Field2" : 1
                ,
                "indexName" : "Field2_1",
                "isMultiKey" : false,
                "multiKeyPaths" : 
                    "Field2" : [ ]
                ,
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : 
                    "Field2" : [
                        "[\"data on field2: 5\", \"data on field2: 5\"]"
                    ]
                ,
                "keysExamined" : 1,
                "seeks" : 1,
                "dupsTested" : 0,
                "dupsDropped" : 0,
                "seenInvalidated" : 0
            
        
    ,
    "serverInfo" : 
    ...
    ,
    "ok" : 1,
    ...

【讨论】:

我是这么想的,但不是。查询有投影时的输出: "stage" : "PROJECTION", ... "stage" : "FETCH", // [NOT CHILD] "stage" : "IXSCAN", // [CHILD] 你在使用排序规则吗? @Rafael,您是否使用 Adam 提供的“executionStats”运行了相同的查询? 谢谢...它起作用了我意识到投影不应包含“_id”以避免 FETCH。 现在清楚了……索引没有“_id”,那就不会被覆盖了。

以上是关于当 INDEX 匹配时,MongoDB 阶段是不是应该避免 FETCH?的主要内容,如果未能解决你的问题,请参考以下文章

在 go mongodb 中的 From Table 上匹配阶段

MongoDB 索引文本搜索仅适用于完全匹配

MongoDB 聚合中的多个 $project 阶段是不是会影响性能

为啥在与数组中的字段匹配时,mongoDB聚合中的查找中的管道不起作用?

使用Mongoose时,为什么在index.js中MongoDB不是必需的?

Mongodb笔记 Index