Производительность многоключевого индекса MongoDB

Фон

У меня есть группа пользователей с такой структурой документов:

{
    "_id" : ObjectId("54e61137cca5d2ff0a8b4567"),
    "login" : "test1",
    "emails" : [
        {
            "email" : "[email protected]",
            "is_primary" : true,
            "_id" : ObjectId("57baf3e97323afb2688e639c")
        },
        {
            "email" : "[email protected]",
            "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
}

Как вы можете видеть, executeStats.executionStages.inputStage.nReturned имеет значение 0, а значение executionStats.totalDocsExamined равно нулю. Все в порядке, я думаю, что нет документов с логином, подобным введенному. Но если я хочу искать пользователей по электронной почте, я сделаю следующее:

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
}

И здесь executeStats.executionStages.inputStage.nReturned (и executionStats.totalDocsExamined) равно 698993 (ExecutionStats.nReturned равно 0, как в первом запросе)

Вопрос

Почему, когда я использую поиск с многоклавишным индексом (users.user) на этапе ixscan, возвращает всю мою коллекцию, а на этапе выборки происходит вся коллекция. Но если я использую поиск по индексу без нескольких ключей (вход в систему), этап ixscan сканирует ожидаемые значения, а на этапе выборки я даю то, что хочу.

UPD: когда я использую регулярное выражение не как / smth /, а / ^ smth /, то сканирование по электронной почте. Поле электронной почты также возвращает 0 элементов. Почему многоключевой и обычный индекс дают разные результаты для регулярного выражения, такого как / smth /?


person lis-dev    schedule 27.10.2016    source источник


Ответы (1)


Потому что это многоклавишный индекс. объяснено здесь

Когда фильтр запроса указывает точное совпадение для массива в целом, MongoDB может использовать многоклавишный индекс для поиска первого элемента массива запроса, но не может использовать многоклавишное сканирование индекса для поиска всего массива. Вместо этого, после использования многоключевого индекса для поиска первого элемента массива запроса, MongoDB извлекает связанные документы и фильтрует документы, массив которых соответствует массиву в запросе.

person JJussi    schedule 28.10.2016