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

Raf*_*ael 3 indexing mongodb

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

\n

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

\n
\n

覆盖查询 当索引覆盖查询时,MongoDB 既可以匹配查询条件,又可以仅使用索引键返回结果;\ni.e. MongoDB 不需要检查集合中的文档来\n返回结果。

\n

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

\n

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

\n
\n

和这个

\n
\n

这样,查询耗时不到 2 毫秒。由于索引\n\xe2\x80\x98 覆盖了\xe2\x80\x99 查询,MongoDB 能够匹配查询条件\n并仅使用索引返回结果钥匙;甚至不需要检查集合中的文档来返回结果。(如果您在执行计划中看到 IXSCAN 阶段不是 FETCH 阶段的子级,则索引 \xe2\x80\x98 覆盖\xe2\x80\x99 查询。)( https://studio3t.com /知识库/文章/mongodb-index-strategy/

\n
\n

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

\n

测试示例:

\n
    db.Test.insert({"Field1":"data on field1: 1","Field2":"data on field2: 1"});\n    db.Test.insert({"Field1":"data on field1: 2","Field2":"data on field2: 2"});\n    db.Test.insert({"Field1":"data on field1: 3","Field2":"data on field2: 3"});\n    db.Test.insert({"Field1":"data on field1: 4","Field2":"data on field2: 4"});\n    db.Test.insert({"Field1":"data on field1: 5","Field2":"data on field2: 5"});\n    db.Test.insert({"Field1":"data on field1: 6","Field2":"data on field2: 6"});\n
Run Code Online (Sandbox Code Playgroud)\n

在我创建了 Field2 的索引之后。

\n
db.Test.createIndex({"Field2":1})\n
Run Code Online (Sandbox Code Playgroud)\n

然后我查询集合:

\n
db.Test.find({"Field2":"data on field2: 5"}).explain("executionStats");\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
[...]\n"winningPlan" : {\n            "stage" : "FETCH",\n            "inputStage" : {\n                "stage" : "IXSCAN",\n                "keyPattern" : {\n                    "Campo2" : 1.0\n                },\n                "indexName" : "Field2_1",\n                "isMultiKey" : false,\n                "multiKeyPaths" : {\n                    "Campo2" : []\n                },\n                "isUnique" : false,\n                "isSparse" : false,\n                "isPartial" : false,\n                "indexVersion" : 2,\n                "direction" : "forward",\n                "indexBounds" : {\n                    "Field2" : [ \n                        "[\\"data on field2: 5", \\"data on field2: 5\\"]"\n                    ]\n                }\n            }\n        },\n[...]\n
Run Code Online (Sandbox Code Playgroud)\n

有两个阶段:一个“阶段”:“FETCH”,及其子“阶段”:“IXSCAN”

\n

谁能解释一下我的误解是什么?

\n

*** 关于投影

\n

使用投影运行查询时

\n
"winningPlan" : {\n            "stage" : "PROJECTION",\n            "transformBy" : {\n                "Campo2" : 1.0\n            },\n            "inputStage" : {\n                "stage" : "FETCH",\n                "inputStage" : {\n                    "stage" : "IXSCAN",\n                    "keyPattern" : {\n                        "Field2" : 1.0\n                    },\n                    "indexName" : "Field2_1",\n                    "isMultiKey" : false,\n                    "multiKeyPaths" : {\n                        "Campo2" : []\n                    },\n                    "isUnique" : false,\n                    "isSparse" : false,\n                    "isPartial" : false,\n                    "indexVersion" : 2,\n                    "direction" : "forward",\n                    "indexBounds" : {\n                        "Field2" : [ \n                            "[\\"data on field2: 5", \\"data on field2: 5\\"]"\n                        ]\n                    }\n                }\n            }\n        },\n
Run Code Online (Sandbox Code Playgroud)\n

Adam 的回答是:成功了!

\n

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

\n

Ada*_*son 6

您的查询未指定投影,这意味着它将返回文档中的所有字段。这意味着{ Field2: 1 }索引不覆盖查询,因为它只包含单个字段。

下面的查询应该被完全覆盖并且不应该有 FETCH 阶段。请注意,投影明确排除了该_id字段,因为除非另有指定,否则该字段将包含在投影中

    db.Test.find(
      {"Field2":"data on field2: 5"},
      {"Field2" : 1, "_id" : 0 }
    ).explain("executionStats");
Run Code Online (Sandbox Code Playgroud)

输出:

{
    "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,
    ...
}
Run Code Online (Sandbox Code Playgroud)

  • 现在很明显......索引没有“_id”,那么它就不会被覆盖。 (2认同)