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

17

У меня есть коллекция 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
}
}

ссылка на статистику

    
задан Eliezer 23.11.2015 в 12:43
источник
  • можете ли вы также опубликовать раздел «queryPlanner» для объяснения () для обоих запросов? –  joao 26.11.2015 в 14:03
  • Я добавил ссылки на все «объяснения» результатов –  Eliezer 26.11.2015 в 18:18
  • Согласно новым полным файлам объяснения, оба запроса занимают одинаковое количество времени (около 1800 мс). Можете ли вы опубликовать все индексы, которые у вас есть для этой коллекции (есть разные индексы, используемые в поставленных вами статьях, но не в связанных с ними файлах)? –  joao 26.11.2015 в 18:38
  • Вы всегда можете попытаться принудительно использовать конкретное использование индекса с помощью «подсказки» (если indexFilterSet является ложным, как в вашем случае, иначе подсказка будет проигнорирована). Можете ли вы проверить, соответствует ли время выполнения этого времени? –  joao 26.11.2015 в 18:50
  • Извините, я установил ссылку на неправильный «объясняющий» вывод для запроса «Cafe Ne». Теперь это правильно. –  Eliezer 26.11.2015 в 23:17
Показать остальные комментарии

3 ответа

10

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

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

  

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

Это из собственной документации MongoDB об индицировании .

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

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

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

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

    
ответ дан Oz123 29.11.2015 в 17:15
  • Размер индекса не является проблемой. Для существующих документов ответ возвращается очень быстро. Проблема в том, что, когда он не находит документ по индексу, он сканирует десятки тысяч документов вместо того, чтобы сдаваться и не возвращать никаких результатов. –  Eliezer 30.11.2015 в 11:34
  • Что касается количества категорий на документ, в среднем на документ приходится около 1,1 категории, поэтому он не сильно раздувает индекс. Условие «$ in» истинно, когда документ содержит по меньшей мере одну категорию списка в запросе –  Eliezer 30.11.2015 в 11:34
  • @Eliezer, если ваш поиск нечеткий, требуется время для поиска индекса. Можете ли вы опубликовать размеры своего индекса? –  Oz123 30.11.2015 в 11:38
  • Я добавил в конце вопроса ссылку на вывод geoData.stats (). Индекс «loc_2dsphere_categoriesIds_1_name_1» примерно в 2,5 раза больше, чем индекс «id» –  Eliezer 30.11.2015 в 12:08
  • На самом деле, я провел некоторое тестирование. С документами, сгенерированными с помощью этого шаблона mgenerate, средний размер записи индекса составляет 3732 байта, с суммарным размером индекса 26,1 ГБ. Я думаю, что решает это подзапрос. Расширяясь на @ Oz123, я бы предположил, что рабочий набор (иначе говоря, «существующие» документы) может содержать большинство документов, которые будут возвращены, а курсор (который лениво загружает документы, iirc) просто читает оставшиеся в фоновом режиме. Без соответствия ни один курсор не может вернуться после обнаружения первых документов, поэтому у нас есть некоторая операция с диском –  Markus W Mahlberg 01.12.2015 в 13:13
2

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

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

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

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

ответ дан Laizer 30.11.2015 в 07:38
1

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

{
  "loc" : "2dsphere",
  "name" : 1,
  "categoriesIds" : 1
}
    
ответ дан jbmartinez 02.12.2015 в 15:00