【问题标题】:MongoDB multikey index performanceMongoDB多键索引性能
【发布时间】:2016-10-27 09:14:26
【问题描述】:

背景

我有一组用户的文档结构如下:

{
    "_id" : ObjectId("54e61137cca5d2ff0a8b4567"),
    "login" : "test1",
    "emails" : [
        {
            "email" : "test1@example.com",
            "is_primary" : true,
            "_id" : ObjectId("57baf3e97323afb2688e639c")
        },
        {
            "email" : "test1_1@example.com",
            "is_primary" : false,
            "_id" : ObjectId("57baf3e97323afb2688e639d")
        }
    ]
}

索引:

{
    "v" : 1,
    "key" : {
        "login" : 1
    },
    "name" : "login_1",
    "ns" : "mydb.users",
    "background" : true
},
{
    "v" : 1,
    "key" : {
        "emails.email" : 1
    },
    "name" : "emails.email_1",
    "ns" : "mydb.users"
}

文档数约为 700000

场景

为了解释通过登录搜索用户,我这样做:

rs0:PRIMARY> db.users.explain('executionStats').find({'login' : /test123123123/})
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "mydb.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "login" : /test123123123/
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "filter" : {
                    "login" : /test123123123/
                },
                "keyPattern" : {
                    "login" : 1
                },
                "indexName" : "login_1",
                "isMultiKey" : false,
                "direction" : "forward",
                "indexBounds" : {
                    "login" : [
                        "[\"\", {})",
                        "[/test123123123/, /test123123123/]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 0,
        "executionTimeMillis" : 1040,
        "totalKeysExamined" : 698993,
        "totalDocsExamined" : 0,
        "executionStages" : {
            "stage" : "FETCH",
            "nReturned" : 0,
            "executionTimeMillisEstimate" : 930,
            "works" : 698994,
            "advanced" : 0,
            "needTime" : 698993,
            "needFetch" : 0,
            "saveState" : 5460,
            "restoreState" : 5460,
            "isEOF" : 1,
            "invalidates" : 0,
            "docsExamined" : 0,
            "alreadyHasObj" : 0,
            "inputStage" : {
                "stage" : "IXSCAN",
                "filter" : {
                    "login" : /test123123123/
                },
                "nReturned" : 0,
                "executionTimeMillisEstimate" : 920,
                "works" : 698993,
                "advanced" : 0,
                "needTime" : 698993,
                "needFetch" : 0,
                "saveState" : 5460,
                "restoreState" : 5460,
                "isEOF" : 1,
                "invalidates" : 0,
                "keyPattern" : {
                    "login" : 1
                },
                "indexName" : "login_1",
                "isMultiKey" : false,
                "direction" : "forward",
                "indexBounds" : {
                    "login" : [
                        "[\"\", {})",
                        "[/test123123123/, /test123123123/]"
                    ]
                },
                "keysExamined" : 698993,
                "dupsTested" : 0,
                "dupsDropped" : 0,
                "seenInvalidated" : 0,
                "matchTested" : 0
            }
        }
    },
    "serverInfo" : {
        "host" : "myhost",
        "port" : 27017,
        "version" : "3.0.12",
        "gitVersion" : "33934938e0e95d534cebbaff656cde916b9c3573"
    },
    "ok" : 1
}

如您所见,executionStats.executionStages.inputStage.nReturned 为 0,executionStats.totalDocsExamined 为 0。没关系,我猜没有像输入的 login 的文档。但是,如果我想通过电子邮件搜索用户,我接下来会这样做:

rs0:PRIMARY> db.users.explain('executionStats').find({'emails.email' : /test123123123/})
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "mydb.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "emails.email" : /test123123123/
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "filter" : {
                "emails.email" : /test123123123/
            },
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "emails.email" : 1
                },
                "indexName" : "emails.email_1",
                "isMultiKey" : true,
                "direction" : "forward",
                "indexBounds" : {
                    "emails.email" : [
                        "[\"\", {})",
                        "[/test123123123/, /test123123123/]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 0,
        "executionTimeMillis" : 7666,
        "totalKeysExamined" : 699016,
        "totalDocsExamined" : 698993,
        "executionStages" : {
            "stage" : "FETCH",
            "filter" : {
                "emails.email" : /test123123123/
            },
            "nReturned" : 0,
            "executionTimeMillisEstimate" : 7355,
            "works" : 699017,
            "advanced" : 0,
            "needTime" : 699016,
            "needFetch" : 0,
            "saveState" : 5462,
            "restoreState" : 5462,
            "isEOF" : 1,
            "invalidates" : 0,
            "docsExamined" : 698993,
            "alreadyHasObj" : 0,
            "inputStage" : {
                "stage" : "IXSCAN",
                "nReturned" : 698993,
                "executionTimeMillisEstimate" : 1630,
                "works" : 699016,
                "advanced" : 698993,
                "needTime" : 23,
                "needFetch" : 0,
                "saveState" : 5462,
                "restoreState" : 5462,
                "isEOF" : 1,
                "invalidates" : 0,
                "keyPattern" : {
                    "emails.email" : 1
                },
                "indexName" : "emails.email_1",
                "isMultiKey" : true,
                "direction" : "forward",
                "indexBounds" : {
                    "emails.email" : [
                        "[\"\", {})",
                        "[/test123123123/, /test123123123/]"
                    ]
                },
                "keysExamined" : 699016,
                "dupsTested" : 699016,
                "dupsDropped" : 23,
                "seenInvalidated" : 0,
                "matchTested" : 0
            }
        }
    },
    "serverInfo" : {
        "host" : "myhost",
        "port" : 27017,
        "version" : "3.0.12",
        "gitVersion" : "33934938e0e95d534cebbaff656cde916b9c3573"
    },
    "ok" : 1
}

这里 executionStats.executionStages.inputStage.nReturned(和 executionStats.totalDocsExamined)等于 698993(executionStats.nReturned 是 0,就像第一个查询一样)

问题

为什么当我在 ixscan 阶段使用多键索引 (users.user) 搜索时会返回我的所有集合,而 fetch 阶段会发生所有集合。但是,如果我使用非多键索引(登录)搜索,ixscan 阶段会扫描预期值,而在 fetch 阶段我会给出我想要的。

UPD:当我使用正则表达式时,不是 /smth/,而是 /^smth/,然后通过 emails.email 字段扫描返回 0 个元素。为什么多键和普通索引会给我像 /smth/ 这样的正则表达式不同的结果?

【问题讨论】:

    标签: mongodb mongodb-query database-indexes mongodb-indexes multikey


    【解决方案1】:

    因为它是多键索引。 explained here

    当查询过滤器为整个数组指定精确匹配时,MongoDB 可以使用多键索引查找查询数组的第一个元素,但不能使用多键索引扫描查找整个数组。相反,在使用多键索引查找查询数组的第一个元素后,MongoDB 会检索关联的文档,并筛选其数组与查询中的数组匹配的文档。

    【讨论】:

      猜你喜欢
      • 2013-09-05
      • 2015-03-04
      • 1970-01-01
      • 1970-01-01
      • 2017-02-07
      • 2012-06-09
      • 2011-11-15
      • 2016-07-10
      • 2013-09-16
      相关资源
      最近更新 更多