Dette er at søge nålen i en høstak. Vi har brug for noget output af explain()
for de forespørgsler, der ikke fungerer godt. Desværre ville selv det kun løse problemet for den specifikke forespørgsel, så her er en strategi for, hvordan du griber dette an:
- Sørg for, at det ikke er på grund af utilstrækkelig RAM og overdreven personsøgning
- Aktiver DB-profiler (ved hjælp af
db.setProfilingLevel(1, timeout)
hvortimeout
er tærsklen for antallet af millisekunder, forespørgslen eller kommandoen tager, vil alt langsommere blive logget) - Undersøg de langsomme forespørgsler i
db.system.profile
og kør forespørgslerne manuelt ved hjælp afexplain()
- Prøv at identificere de langsomme operationer i
explain()
output, såsomscanAndOrder
eller stornscannet
osv. - Begrundelse for forespørgslens selektivitet og om det er muligt at forbedre forespørgslen ved hjælp af et indeks overhovedet . Hvis ikke, kan du overveje at afvise filterindstillingen for slutbrugeren eller give ham en advarselsdialog om, at handlingen kan være langsom.
Et centralt problem er, at du tilsyneladende tillader dine brugere at kombinere filtre efter behag. Uden indekskrydsning vil det sprænge antallet af påkrævede indekser dramatisk.
Det er også en meget dårlig strategi at blindt kaste et indeks ved enhver mulig forespørgsel. Det er vigtigt at strukturere forespørgslerne og sikre, at de indekserede felter har tilstrækkelig selektivitet .
Lad os sige, at du har en forespørgsel til alle brugere med status
"aktiv" og nogle andre kriterier. Men af de 5 millioner brugere er 3 millioner aktive og 2 millioner ikke, så over 5 millioner poster er der kun to forskellige værdier. Sådan et indeks hjælper normalt ikke. Det er bedre at søge efter de andre kriterier først og derefter scanne resultaterne. I gennemsnit, når du returnerer 100 dokumenter, skal du scanne 167 dokumenter, hvilket ikke vil skade ydeevnen så meget. Men det er ikke så enkelt. Hvis det primære kriterium er joined_at
datoen for brugeren og sandsynligheden for, at brugerne stopper med at bruge tiden er høj, kan du ende med at skulle scanne tusindvis af dokumenter, før man finder hundrede matcher.
Så optimeringen afhænger meget af dataene (ikke kun dens struktur , men også selve dataene ), dets interne korrelationer og dine forespørgselsmønstre .
Tingene bliver værre, når dataene er for store til RAM'en, for så er det fantastisk at have et indeks, men scanning (eller endda bare returnering) af resultaterne kan kræve at hente en masse data fra disken tilfældigt, hvilket tager meget tid.
Den bedste måde at kontrollere dette på er at begrænse antallet af forskellige forespørgselstyper, forbyde forespørgsler på oplysninger med lav selektivitet og forsøge at forhindre tilfældig adgang til gamle data.
Hvis alt andet fejler, og hvis du virkelig har brug for så meget fleksibilitet i filtre, kan det være umagen værd at overveje en separat søge-DB, der understøtter indekskryds, hente mongo-id'erne derfra og derefter få resultaterne fra mongo ved hjælp af $in . Men det er fyldt med sine egne farer.
-- REDIGER --
Forklaringen, du postede, er et smukt eksempel på problemet med at scanne felter med lav selektivitet. Tilsyneladende er der en masse dokumenter til "[email protected]". Nu er det ret hurtigt at finde disse dokumenter og sortere dem faldende efter tidsstempel, fordi det understøttes af indekser med høj selektivitet. Da der kun er to enhedstyper, skal mongo desværre scanne 30060 dokumenter for at finde den første, der matcher 'mobil'.
Jeg antager, at dette er en form for websporing, og brugerens brugsmønster gør forespørgslen langsom (ville han skifte mobil og web på daglig basis, ville forespørgslen være hurtig).
At gøre denne særlige forespørgsel hurtigere kunne gøres ved hjælp af et sammensat indeks, der indeholder enhedstypen, f.eks. ved hjælp af
a) ensureIndex({'username': 1, 'userAgent.deviceType' : 1, 'timestamp' :-1})
eller
b) ensureIndex({'userAgent.deviceType' : 1, 'username' : 1, 'timestamp' :-1})
Desværre betyder det, at forespørgsler som find({"brugernavn" :"foo"}).sort({"tidsstempel" :-1});
kan ikke bruge det samme indeks længere, så som beskrevet vil antallet af indeks vokse meget hurtigt.
Jeg er bange for, at der ikke er nogen særlig god løsning på dette ved at bruge mongodb på nuværende tidspunkt.