Това е търсене на иглата в купа сено. Ще ни трябва някакъв изход на explain()
за тези заявки, които не се представят добре. За съжаление, дори това ще реши проблема само за тази конкретна заявка, така че ето стратегия как да се подходи към това:
- Уверете се, че не е поради недостатъчна RAM и прекомерно пейджинг
- Активирайте инструмента за профилиране на DB (използвайки
db.setProfilingLevel(1, timeout)
къдетоизчакване
е прагът за броя милисекунди, необходими на заявката или командата, всичко по-бавно ще бъде регистрирано) - Проверете бавните заявки в
db.system.profile
и стартирайте заявките ръчно, като използватеexplain()
- Опитайте се да идентифицирате бавните операции в
explain()
изход, катоscanAndOrder
или голямnscanned
и др. - Причина за селективността на заявката и дали е възможно да се подобри заявката с помощта на индекс изобщо . Ако не, помислете дали да не разрешите настройката на филтъра за крайния потребител или му дайте предупредителен диалогов прозорец, че операцията може да е бавна.
Ключов проблем е, че очевидно позволявате на потребителите си да комбинират филтри по желание. Без пресичане на индекси, това ще увеличи драстично броя на необходимите индекси.
Освен това сляпото хвърляне на индекс към всяка възможна заявка е много лоша стратегия. Важно е да структурирате заявките и да се уверите, че индексираните полета имат достатъчна селективност .
Да приемем, че имате заявка за всички потребители с status
"активен" и някои други критерии. Но от 5 милиона потребители 3 милиона са активни и 2 милиона не, така че над 5 милиона записа има само две различни стойности. Такъв индекс обикновено не помага. По-добре е първо да потърсите другите критерии, след това да сканирате резултатите. Средно, когато връщате 100 документа, ще трябва да сканирате 167 документа, което няма да навреди на производителността. Но не е толкова просто. Ако основният критерий е joined_at
датата на потребителя и вероятността потребителите да спрат употребата с времето е голяма, може да се наложи да сканирате хиляди на документи, преди да намеря сто съвпадения.
Така че оптимизацията зависи много от данните (не само от тяхната структура). , но и самите днани ), неговите вътрешни корелации и вашите модели на заявки .
Нещата се влошават, когато данните са твърде големи за RAM, защото тогава наличието на индекс е страхотно, но сканирането (или дори просто връщането) на резултатите може да изисква извличане на много данни от диска на случаен принцип, което отнема много време.
Най-добрият начин да контролирате това е да ограничите броя на различните типове заявки, да забраните заявки за информация с ниска селективност и да се опитате да предотвратите произволен достъп до стари данни.
Ако всичко друго се провали и ако наистина се нуждаете от толкова голяма гъвкавост във филтрите, може да си струва да помислите за отделна база данни за търсене, която поддържа пресичане на индекси, извлечете идентификаторите на mongo от там и след това получите резултатите от mongo с помощта на $inкод> . Но това е изпълнено със собствени опасности.
-- РЕДАКТИРАНЕ --
Обяснението, което публикувахте, е красив пример за проблема със сканирането на полета с ниска селективност. Очевидно има много документи за "[email protected]". Сега намирането на тези документи и сортирането им по низходящо време е доста бързо, защото се поддържа от индекси с висока селективност. За съжаление, тъй като има само два типа устройства, mongo трябва да сканира 30060 документа, за да намери първия, който съответства на „мобилно“.
Предполагам, че това е някакъв вид уеб проследяване и моделът на използване на потребителя прави заявката бавна (ако той би превключил мобилно и уеб на дневна база, заявката ще бъде бърза).
Ускоряването на тази конкретна заявка може да се направи с помощта на съставен индекс, който съдържа типа на устройството, напр. използвайки
a) ensureIndex({'username': 1, 'userAgent.deviceType' : 1, 'timestamp' :-1})
или
b) ensureIndex({'userAgent.deviceType' : 1, 'username' : 1, 'timestamp' :-1})
За съжаление, това означава, че заявки като find({"username" :"foo"}).sort({"timestamp" :-1});
не може повече да използва същия индекс, така че, както е описано, броят на индексите ще расте много бързо.
Боя се, че в момента няма много добро решение за това с помощта на mongodb.