Dit is zoeken naar de speld in een hooiberg. We hebben wat uitvoer nodig van explain()
voor die zoekopdrachten die niet goed presteren. Helaas zou zelfs dat het probleem alleen voor die specifieke zoekopdracht oplossen, dus hier is een strategie om dit aan te pakken:
- Zorg ervoor dat dit niet komt door onvoldoende RAM en overmatige paging
- Schakel de DB-profiler in (met behulp van
db.setProfilingLevel(1, timeout)
waartimeout
is de drempel voor het aantal milliseconden dat de query of opdracht duurt, alles wat langzamer is wordt gelogd) - Inspecteer de trage zoekopdrachten in
db.system.profile
en voer de zoekopdrachten handmatig uit met behulp vanexplain()
- Probeer de trage bewerkingen te identificeren in de
explain()
uitvoer, zoalsscanAndOrder
of grotenscanned
, enz. - Reden over de selectiviteit van de zoekopdracht en of het mogelijk is om de zoekopdracht te verbeteren met behulp van een index al dan niet . Als dat niet het geval is, kunt u overwegen de filterinstelling voor de eindgebruiker niet toe te staan of hem een waarschuwingsvenster te geven dat de bewerking mogelijk traag is.
Een belangrijk probleem is dat u uw gebruikers blijkbaar toestaat filters naar believen te combineren. Zonder indexkruising zal dat het aantal vereiste indexen drastisch doen toenemen.
Ook is het blindelings gooien van een index bij elke mogelijke zoekopdracht een zeer slechte strategie. Het is belangrijk om de zoekopdrachten te structureren en ervoor te zorgen dat de geïndexeerde velden voldoende selectiviteit hebben .
Stel dat u een vraag heeft voor alle gebruikers met status
"actief" en enkele andere criteria. Maar van de 5 miljoen gebruikers zijn er 3 miljoen actief en 2 miljoen niet, dus meer dan 5 miljoen inzendingen zijn er slechts twee verschillende waarden. Zo'n index helpt meestal niet. Het is beter om eerst naar de andere criteria te zoeken en vervolgens de resultaten te scannen. Gemiddeld moet je bij het retourneren van 100 documenten 167 documenten scannen, wat de prestaties niet al te erg zal schaden. Maar zo eenvoudig is het niet. Als het primaire criterium de joined_at
. is datum van de gebruiker en de kans dat gebruikers het gebruik na verloop van tijd beëindigen groot is, kan het zijn dat u uiteindelijk duizenden moet scannen van documenten voordat je honderd overeenkomsten vindt.
De optimalisatie hangt dus sterk af van de gegevens (niet alleen de structuur , maar ook de gegevens zelf ), de interne correlaties en uw querypatronen .
Het wordt nog erger als de gegevens te groot zijn voor het RAM-geheugen, want dan is het hebben van een index geweldig, maar het scannen (of zelfs gewoon retourneren) van de resultaten kan vereisen dat er willekeurig veel gegevens van de schijf worden opgehaald, wat veel tijd kost.
De beste manier om dit te beheersen, is door het aantal verschillende soorten zoekopdrachten te beperken, zoekopdrachten op informatie met een lage selectiviteit niet toe te staan en willekeurige toegang tot oude gegevens te voorkomen.
Als al het andere faalt en als je echt zoveel flexibiliteit in filters nodig hebt, is het misschien de moeite waard om een aparte zoek-DB te overwegen die indexkruisingen ondersteunt, de mongo-ID's daar op te halen en vervolgens de resultaten van mongo te krijgen met behulp van $in
. Maar dat is beladen met zijn eigen gevaren.
-- BEWERK --
De uitleg die je hebt gepost is een mooi voorbeeld van het probleem met het scannen van velden met een lage selectiviteit. Blijkbaar zijn er veel documenten voor "[email protected]". Nu gaat het vinden van die documenten en het aflopend sorteren op tijdstempel vrij snel, omdat het wordt ondersteund door indexen met een hoge selectiviteit. Omdat er maar twee apparaattypen zijn, moet mongo helaas 30060 documenten scannen om de eerste te vinden die overeenkomt met 'mobiel'.
Ik neem aan dat dit een soort webtracking is en dat het gebruikspatroon van de gebruiker de zoekopdracht traag maakt (zou hij dagelijks van mobiel naar internet wisselen, dan zou de zoekopdracht snel zijn).
Het sneller maken van deze specifieke zoekopdracht kan worden gedaan met behulp van een samengestelde index die het apparaattype bevat, b.v. met behulp van
a) ensureIndex({'username': 1, 'userAgent.deviceType' : 1, 'timestamp' :-1})
of
b) ensureIndex({'userAgent.deviceType' : 1, 'username' : 1, 'timestamp' :-1})
Helaas betekent dit dat zoekopdrachten zoals find({"username" : "foo"}).sort({"timestamp" : -1});
kan dezelfde index niet meer gebruiken, dus, zoals beschreven, zal het aantal indexen erg snel groeien.
Ik ben bang dat er op dit moment geen goede oplossing is voor het gebruik van mongodb.