Подтвердить что ты не робот

Mongodb - низкая производительность, когда результаты не возвращаются

У меня есть коллекция Mongodb с около 7 миллионами документов, которые представляют места.

Я запускаю запрос, который ищет места, имена которых начинаются с префикса рядом с определенным местом.

У нас есть составной индекс, как описано ниже, чтобы ускорить поиск.

Когда поисковый запрос находит совпадение (даже если только один), запрос выполняется очень быстро (~ 20 milisec). Но когда нет совпадения, для выполнения запроса может потребоваться 30 секунд.

Помогите.

Подробно:

Каждое место (геоданные) имеет следующие поля:

"loc" - a GeoJSON point that represent the location
"categoriesIds" - array of int ids
"name" - the name of the placee

В этой коллекции указан следующий индекс:

{
  "loc" : "2dsphere",
  "categoriesIds" : 1,
  "name" : 1
}

Запрос:

db.geoData.find({
  "loc":{
    "$near":{
      "$geometry":{
        "type": "Point" ,
        "coordinates": [ -0.10675191879272461 , 51.531600743186644]
      },
      "$maxDistance": 5000.0
    }
  }, 
  "categoriesIds":{
    "$in": [ 1 , 2 , 71 , 70 , 74 , 72 , 73 , 69 , 44 , 26 , 27 , 33 , 43 , 45 , 53 , 79]
  }, 
  "name":{ "$regex": "^Cafe Ne"}
})

Статистика выполнения (Ссылка на весь результат объяснения)

    "executionStats" : {
    "executionSuccess" : true,
    "nReturned" : 1,
    "executionTimeMillis" : 169,
    "totalKeysExamined" : 14333,
    "totalDocsExamined" : 1,
    "executionStages" : {
        "stage" : "GEO_NEAR_2DSPHERE",
        "nReturned" : 1,
        "executionTimeMillisEstimate" : 60,
        "works" : 14354,
        "advanced" : 1,
        "needTime" : 14351,
        "needFetch" : 0,
        "saveState" : 361,
        "restoreState" : 361,
        "isEOF" : 1,
        "invalidates" : 0,
        "keyPattern" : {
            "loc" : "2dsphere",
            "categoriesIds" : 1,
            "name" : 1
        },
        "indexName" : "loc_2dsphere_categoriesIds_1_name_1",
        "searchIntervals" : [ 
            {
                "minDistance" : 0,
                "maxDistance" : 3408.329295346151,
                "maxInclusive" : false
            }, 
            {
                "minDistance" : 3408.329295346151,
                "maxDistance" : 5000,
                "maxInclusive" : true
            }
        ],
        "inputStages" : [ 
            {
                "stage" : "FETCH",
                "nReturned" : 1,
                "executionTimeMillisEstimate" : 20,
                "works" : 6413,
                "advanced" : 1,
                "needTime" : 6411,
                "needFetch" : 0,
                "saveState" : 361,
                "restoreState" : 361,
                "isEOF" : 1,
                "invalidates" : 0,
                "docsExamined" : 1,
                "alreadyHasObj" : 0,
                "inputStage" : {
                    "stage" : "IXSCAN",
                    "filter" : {
                        "TwoDSphereKeyInRegionExpression" : true
                    },
                    "nReturned" : 1,
                    "executionTimeMillisEstimate" : 20,
                    "works" : 6413,
                    "advanced" : 1,
                    "needTime" : 6411,
                    "needFetch" : 0,
                    "saveState" : 361,
                    "restoreState" : 361,
                    "isEOF" : 1,
                    "invalidates" : 0,
                    "keyPattern" : {
                        "loc" : "2dsphere",
                        "categoriesIds" : 1,
                        "name" : 1
                    },
                    "indexName" : "loc_2dsphere_categoriesIds_1_name_1",
                    "isMultiKey" : true,
                    "direction" : "forward",
                    "indexBounds" : {
                        "loc" : [ 
                            "[\"2f1003230\", \"2f1003230\"]", 
                            "[\"2f10032300\", \"2f10032300\"]", 
                            "[\"2f100323000\", \"2f100323000\"]", 
                            "[\"2f1003230001\", \"2f1003230001\"]", 
                            "[\"2f10032300012\", \"2f10032300013\")", 
                            "[\"2f1003230002\", \"2f1003230002\"]", 
                            "[\"2f10032300021\", \"2f10032300022\")", 
                            "[\"2f10032300022\", \"2f10032300023\")", 
                            "[\"2f100323003\", \"2f100323003\"]", 
                            "[\"2f1003230031\", \"2f1003230031\"]", 
                            "[\"2f10032300311\", \"2f10032300312\")", 
                            "[\"2f10032300312\", \"2f10032300313\")", 
                            "[\"2f10032300313\", \"2f10032300314\")", 
                            "[\"2f1003230032\", \"2f1003230032\"]", 
                            "[\"2f10032300320\", \"2f10032300321\")", 
                            "[\"2f10032300321\", \"2f10032300322\")"
                        ],
                        "categoriesIds" : [ 
                            "[1.0, 1.0]", 
                            "[2.0, 2.0]", 
                            "[26.0, 26.0]", 
                            "[27.0, 27.0]", 
                            "[33.0, 33.0]", 
                            "[43.0, 43.0]", 
                            "[44.0, 44.0]", 
                            "[45.0, 45.0]", 
                            "[53.0, 53.0]", 
                            "[69.0, 69.0]", 
                            "[70.0, 70.0]", 
                            "[71.0, 71.0]", 
                            "[72.0, 72.0]", 
                            "[73.0, 73.0]", 
                            "[74.0, 74.0]", 
                            "[79.0, 79.0]"
                        ],
                        "name" : [ 
                            "[\"Cafe Ne\", \"Cafe Nf\")", 
                            "[/^Cafe Ne/, /^Cafe Ne/]"
                        ]
                    },
                    "keysExamined" : 6412,
                    "dupsTested" : 0,
                    "dupsDropped" : 0,
                    "seenInvalidated" : 0,
                    "matchTested" : 1
                }
            }, 
            {
                "stage" : "FETCH",
                "nReturned" : 0,
                "executionTimeMillisEstimate" : 40,
                "works" : 7922,
                "advanced" : 0,
                "needTime" : 7921,
                "needFetch" : 0,
                "saveState" : 261,
                "restoreState" : 261,
                "isEOF" : 1,
                "invalidates" : 0,
                "docsExamined" : 0,
                "alreadyHasObj" : 0,
                "inputStage" : {
                    "stage" : "IXSCAN",
                    "filter" : {
                        "TwoDSphereKeyInRegionExpression" : true
                    },
                    "nReturned" : 0,
                    "executionTimeMillisEstimate" : 40,
                    "works" : 7922,
                    "advanced" : 0,
                    "needTime" : 7921,
                    "needFetch" : 0,
                    "saveState" : 261,
                    "restoreState" : 261,
                    "isEOF" : 1,
                    "invalidates" : 0,
                    "keyPattern" : {
                        "loc" : "2dsphere",
                        "categoriesIds" : 1,
                        "name" : 1
                    },
                    "indexName" : "loc_2dsphere_categoriesIds_1_name_1",
                    "isMultiKey" : true,
                    "direction" : "forward",
                    "indexBounds" : {
                        "loc" : [ 
                            "[\"2f1003230\", \"2f1003230\"]", 
                            "[\"2f10032300\", \"2f10032300\"]", 
                            "[\"2f100323000\", \"2f100323000\"]", 
                            "[\"2f1003230001\", \"2f1003230001\"]", 
                            "[\"2f10032300011\", \"2f10032300012\")", 
                            "[\"2f10032300012\", \"2f10032300013\")", 
                            "[\"2f1003230002\", \"2f1003230002\"]", 
                            "[\"2f10032300021\", \"2f10032300022\")", 
                            "[\"2f10032300022\", \"2f10032300023\")", 
                            "[\"2f100323003\", \"2f100323003\"]", 
                            "[\"2f1003230031\", \"2f1003230032\")", 
                            "[\"2f1003230032\", \"2f1003230032\"]", 
                            "[\"2f10032300320\", \"2f10032300321\")", 
                            "[\"2f10032300321\", \"2f10032300322\")", 
                            "[\"2f10032300322\", \"2f10032300323\")"
                        ],
                        "categoriesIds" : [ 
                            "[1.0, 1.0]", 
                            "[2.0, 2.0]", 
                            "[26.0, 26.0]", 
                            "[27.0, 27.0]", 
                            "[33.0, 33.0]", 
                            "[43.0, 43.0]", 
                            "[44.0, 44.0]", 
                            "[45.0, 45.0]", 
                            "[53.0, 53.0]", 
                            "[69.0, 69.0]", 
                            "[70.0, 70.0]", 
                            "[71.0, 71.0]", 
                            "[72.0, 72.0]", 
                            "[73.0, 73.0]", 
                            "[74.0, 74.0]", 
                            "[79.0, 79.0]"
                        ],
                        "name" : [ 
                            "[\"Cafe Ne\", \"Cafe Nf\")", 
                            "[/^Cafe Ne/, /^Cafe Ne/]"
                        ]
                    },
                    "keysExamined" : 7921,
                    "dupsTested" : 0,
                    "dupsDropped" : 0,
                    "seenInvalidated" : 0,
                    "matchTested" : 0
                }
            }
        ]
    },

Статистика выполнения при поиске "CafeNeeNNN" вместо "Cafe Ne" (Ссылка на весь результат объяснения)

 "executionStats" : {
    "executionSuccess" : true,
    "nReturned" : 0,
    "executionTimeMillis" : 2537,
    "totalKeysExamined" : 232259,
    "totalDocsExamined" : 162658,
    "executionStages" : {
        "stage" : "FETCH",
        "filter" : {
            "$and" : [ 
                {
                    "name" : /^CafeNeeNNN/
                }, 
                {
                    "categoriesIds" : {
                        "$in" : [ 
                            1, 
                            2, 
                            26, 
                            27, 
                            33, 
                            43, 
                            44, 
                            45, 
                            53, 
                            69, 
                            70, 
                            71, 
                            72, 
                            73, 
                            74, 
                            79
                        ]
                    }
                }
            ]
        },
        "nReturned" : 0,
        "executionTimeMillisEstimate" : 1330,
        "works" : 302752,
        "advanced" : 0,
        "needTime" : 302750,
        "needFetch" : 0,
        "saveState" : 4731,
        "restoreState" : 4731,
        "isEOF" : 1,
        "invalidates" : 0,
        "docsExamined" : 70486,
        "alreadyHasObj" : 70486,
        "inputStage" : {
            "stage" : "GEO_NEAR_2DSPHERE",
            "nReturned" : 70486,
            "executionTimeMillisEstimate" : 1290,
            "works" : 302751,
            "advanced" : 70486,
            "needTime" : 232264,
            "needFetch" : 0,
            "saveState" : 4731,
            "restoreState" : 4731,
            "isEOF" : 1,
            "invalidates" : 0,
            "keyPattern" : {
                "loc" : "2dsphere"
            },
            "indexName" : "loc_2dsphere",
            "searchIntervals" : [ 
                {
                    "minDistance" : 0,
                    "maxDistance" : 3408.329295346151,
                    "maxInclusive" : false
                }, 
                {
                    "minDistance" : 3408.329295346151,
                    "maxDistance" : 5000,
                    "maxInclusive" : true
                }
            ],
            "inputStages" : [ 
                {
                    "stage" : "FETCH",
                    "nReturned" : 44540,
                    "executionTimeMillisEstimate" : 110,
                    "works" : 102690,
                    "advanced" : 44540,
                    "needTime" : 58149,
                    "needFetch" : 0,
                    "saveState" : 4731,
                    "restoreState" : 4731,
                    "isEOF" : 1,
                    "invalidates" : 0,
                    "docsExamined" : 44540,
                    "alreadyHasObj" : 0,
                    "inputStage" : {
                        "stage" : "IXSCAN",
                        "filter" : {
                            "TwoDSphereKeyInRegionExpression" : true
                        },
                        "nReturned" : 44540,
                        "executionTimeMillisEstimate" : 90,
                        "works" : 102690,
                        "advanced" : 44540,
                        "needTime" : 58149,
                        "needFetch" : 0,
                        "saveState" : 4731,
                        "restoreState" : 4731,
                        "isEOF" : 1,
                        "invalidates" : 0,
                        "keyPattern" : {
                            "loc" : "2dsphere"
                        },
                        "indexName" : "loc_2dsphere",
                        "isMultiKey" : false,
                        "direction" : "forward",
                        "indexBounds" : {
                            "loc" : [ 
                                "[\"2f1003230\", \"2f1003230\"]", 
                                "[\"2f10032300\", \"2f10032300\"]", 
                                "[\"2f100323000\", \"2f100323000\"]", 
                                "[\"2f1003230001\", \"2f1003230001\"]", 
                                "[\"2f10032300012\", \"2f10032300013\")", 
                                "[\"2f1003230002\", \"2f1003230002\"]", 
                                "[\"2f10032300021\", \"2f10032300022\")", 
                                "[\"2f10032300022\", \"2f10032300023\")", 
                                "[\"2f100323003\", \"2f100323003\"]", 
                                "[\"2f1003230031\", \"2f1003230031\"]", 
                                "[\"2f10032300311\", \"2f10032300312\")", 
                                "[\"2f10032300312\", \"2f10032300313\")", 
                                "[\"2f10032300313\", \"2f10032300314\")", 
                                "[\"2f1003230032\", \"2f1003230032\"]", 
                                "[\"2f10032300320\", \"2f10032300321\")", 
                                "[\"2f10032300321\", \"2f10032300322\")"
                            ]
                        },
                        "keysExamined" : 102689,
                        "dupsTested" : 0,
                        "dupsDropped" : 0,
                        "seenInvalidated" : 0,
                        "matchTested" : 44540
                    }
                }, 
                {
                    "stage" : "FETCH",
                    "nReturned" : 47632,
                    "executionTimeMillisEstimate" : 250,
                    "works" : 129571,
                    "advanced" : 47632,
                    "needTime" : 81938,
                    "needFetch" : 0,
                    "saveState" : 2556,
                    "restoreState" : 2556,
                    "isEOF" : 1,
                    "invalidates" : 0,
                    "docsExamined" : 47632,
                    "alreadyHasObj" : 0,
                    "inputStage" : {
                        "stage" : "IXSCAN",
                        "filter" : {
                            "TwoDSphereKeyInRegionExpression" : true
                        },
                        "nReturned" : 47632,
                        "executionTimeMillisEstimate" : 230,
                        "works" : 129571,
                        "advanced" : 47632,
                        "needTime" : 81938,
                        "needFetch" : 0,
                        "saveState" : 2556,
                        "restoreState" : 2556,
                        "isEOF" : 1,
                        "invalidates" : 0,
                        "keyPattern" : {
                            "loc" : "2dsphere"
                        },
                        "indexName" : "loc_2dsphere",
                        "isMultiKey" : false,
                        "direction" : "forward",
                        "indexBounds" : {
                            "loc" : [ 
                                "[\"2f1003230\", \"2f1003230\"]", 
                                "[\"2f10032300\", \"2f10032300\"]", 
                                "[\"2f100323000\", \"2f100323000\"]", 
                                "[\"2f1003230001\", \"2f1003230001\"]", 
                                "[\"2f10032300011\", \"2f10032300012\")", 
                                "[\"2f10032300012\", \"2f10032300013\")", 
                                "[\"2f1003230002\", \"2f1003230002\"]", 
                                "[\"2f10032300021\", \"2f10032300022\")", 
                                "[\"2f10032300022\", \"2f10032300023\")", 
                                "[\"2f100323003\", \"2f100323003\"]", 
                                "[\"2f1003230031\", \"2f1003230032\")", 
                                "[\"2f1003230032\", \"2f1003230032\"]", 
                                "[\"2f10032300320\", \"2f10032300321\")", 
                                "[\"2f10032300321\", \"2f10032300322\")", 
                                "[\"2f10032300322\", \"2f10032300323\")"
                            ]
                        },
                        "keysExamined" : 129570,
                        "dupsTested" : 0,
                        "dupsDropped" : 0,
                        "seenInvalidated" : 0,
                        "matchTested" : 47632
                    }
                }
            ]
        }
    },

Индексы в коллекции

{
"0" : {
    "v" : 1,
    "key" : {
        "_id" : 1
    },
    "name" : "_id_",
    "ns" : "wego.geoData"
},
"1" : {
    "v" : 1,
    "key" : {
        "srcId" : 1
    },
    "name" : "srcId_1",
    "ns" : "wego.geoData"
},
"2" : {
    "v" : 1,
    "key" : {
        "loc" : "2dsphere"
    },
    "name" : "loc_2dsphere",
    "ns" : "wego.geoData",
    "2dsphereIndexVersion" : 2
},
"3" : {
    "v" : 1,
    "key" : {
        "name" : 1
    },
    "name" : "name_1",
    "ns" : "wego.geoData"
},
"4" : {
    "v" : 1,
    "key" : {
        "loc" : "2dsphere",
        "categoriesIds" : 1,
        "name" : 1
    },
    "name" : "loc_2dsphere_categoriesIds_1_name_1",
    "ns" : "wego.geoData",
    "2dsphereIndexVersion" : 2
},
"5" : {
    "v" : 1,
    "key" : {
        "loc" : "2dsphere",
        "categoriesIds" : 1,
        "keywords" : 1
    },
    "name" : "loc_2dsphere_categoriesIds_1_keywords_1",
    "ns" : "wego.geoData",
    "2dsphereIndexVersion" : 2
}
}

Ссылка на статистику посещений

4b9b3361

Ответ 1

Я собираюсь рассказать здесь немного, а затем комментарий о вашем дизайне.

Во-первых, когда вы создаете индекс на ключе, у которого есть массив по значению, вы создаете запись для каждого элемента массива:

Чтобы индексировать поле, содержащее значение массива, MongoDB создает индекс ключ для каждого элемента массива.

Это от Собственная документация MongoDB о дебеках.

Итак, если ваша типичная запись больше, чем рука, полная категорий, и у вас есть 7 миллионов записей, ваш индекс огромен, и потребуется также время для сканирования самого индекса, чтобы найти, что индекс не содержит того, что вы ищете. Это все еще быстрее, чем сканирование коллекции, но оно медленнее, чем скорость поиска существующей записи.

Теперь позвольте мне прокомментировать ваш дизайн схемы. Это вопрос стиля, поэтому не стесняйтесь игнорировать эту часть.

У вас есть запись, которая может быть в 17 категориях. Это немного подавляющее и злоупотребление термином category. Категория - это конкретная разделение, способ быстро связать вещь с группой вещей. Какая вещь принадлежит многим группам? Возьмем, например, ваши записи Cafe Ne. Я предполагаю, что в реальном мире - и, пожалуйста, помните, что программирование и приложения в лучшем случае, когда решать реальные проблемы мира - кафе Ne, это либо ресторан, кафе, джаз-бар, обед. Это наверняка не гараж (разве что, кафе означает автомобили на языке, который я не знаю). Я с трудом представляю себе банк или стоматологическую клинику. Я должен был бы приложить усилия, чтобы найти более 10 значимых категорий, которые пользователи ищут в кафе.

Моя точка зрения, хотя mongodb позволяет вам создавать такие вещи, это не значит, что вам нужно. Постарайтесь сократить количество категорий, которые у вас есть, и те, которые вы ищете, и у вас будет гораздо более высокая производительность.

Ответ 2

Как ДжонниХК предложил в комментариях, и Oz123 указал в своем ответе, проблема здесь, по-видимому, является индексом, который стал настолько большим, что он не смог хорошо работать как индекс. Я считаю, что в дополнение к проблеме расширения категории, о которой уже говорилось, упорядочение полей в вашем индексе создает проблемы. Компонентные индексы построены в соответствии с порядком полей, а размещение name после categoriesIds делает более дорогостоящим запрос на name.

Ясно, что вам нужно настроить свои индексы. Именно то, как вы их настраиваете, зависит от типов запросов, которые вы ожидаете от поддержки. В частности, я не уверен, что вы увидите лучшую производительность от составного индекса loc и name, или если вы увидите лучшую производительность от отдельных индексов, одну для loc и одну для name, Сам Mongo немного расплывчатый, когда лучше всего использовать составной индекс и когда лучше всего использовать отдельные индексы и полагаться на пересечение индексов.

Моя интуиция говорит, что отдельные индексы будут работать лучше, но я бы тестировал оба сценария.

Если вы ожидаете, что вам придется запрашивать и по категориям, без полей name или loc, которые могли бы сузить запрос вниз, вероятно, лучше всего создать отдельный индекс categoriesIds.

Ответ 3

Порядок полей в составном индексе очень важен. Трудно диагностировать без доступа к реальным данным и шаблонам использования, но этот ключ может увеличить вероятность совпадения (или не) документа, используя только индекс:

{
  "loc" : "2dsphere",
  "name" : 1,
  "categoriesIds" : 1
}

Ответ 4

Не уверен, что это точно такая же проблема, но у нас была похожая проблема с многоключевым индексом с низкой производительностью, когда результаты не были найдены.

На самом деле это ошибка Монго, которая была исправлена в v3.3.8. https://jira.mongodb.org/browse/SERVER-15086

Мы исправили наши проблемы после обновления Mongo и перестройки индекса.