Når den er i produktion, bør en applikation give et rettidigt svar til brugeren med det formål at forbedre brugerinteraktionen med din applikation. Til tider kan databaseforespørgsler dog begynde at halte, hvorfor det tager længere tid, før et svar når frem til brugeren, eller rettere sagt, at gennemløbsoperationen afsluttes på grund af overskridelse af den fastsatte gennemsnitlige timeout.
I denne blog skal vi lære, hvordan du kan identificere disse problemer i MongoDB, måder at løse dem på, når de opstår, og hvad er de mulige strategier at tage, så dette ikke sker igen.
Ofte er det, der fører til langsomme forespørgselssvar, forringet CPU-kapacitet, der ikke er i stand til at modstå det underliggende arbejdssæt. Arbejdssæt i dette tilfælde er mængden af data og indekser, der vil blive udsat for en gennemstrømningsinstans, som derfor er aktiv på det tidspunkt. Dette overvejes især i kapacitetsplanlægning, når man forventer, at mængden af involverede data vil stige over tid og antallet af brugere, der engagerer sig i din platform.
Identifikation af et problem med langsom forespørgsel
Der er to måder, du kan identificere langsomme forespørgsler på i MongoDB.
- Brug af Profiler
- Brug af db.currentOp()-hjælper
Brug af MongoDB Profiler
Databaseprofiler i MongoDB er en mekanisme til at indsamle detaljerede oplysninger om databasekommandoer, der udføres mod en kørende mongod-instans, dvs.:gennemløbsoperationer (Opret, Læs, Opdater og Slet) og konfigurations- og administrationskommandoer.
Profileren bruger en begrænset samling ved navn system.profile, hvor den skriver alle data. Det betyder, at når samlingen størrelsesmæssigt er fuld, slettes de ældre dokumenter for at give plads til nye data.
Profilen er slået fra som standard, men afhængigt af profileringsniveauet kan man aktivere det på en per-database eller per instans. De mulige profileringsniveauer er:
- 0 - profileren er slukket og indsamler derfor ingen data.
- 1 - profileren indsamler data for operationer, der tager længere tid end værdien af langsommere hastigheder
- 2- profileren indsamler data for alle operationer.
Men aktivering af profilering genererer en ydeevnepåvirkning på databasen og diskbrug, især når profileringsniveauet er indstillet til 2 . Man bør overveje eventuelle præstationsimplikationer, før man aktiverer og konfigurerer profileren på en produktionsimplementering.
For at indstille profileringen bruger vi db.setProfilingLevel()-hjælperen, såsom:
db.setProfilingLevel(2)
Et eksempeldokument, der vil blive gemt i system.profile-samlingen, vil være:
{ "was" : 0, "slowms" : 100, "sampleRate" : 1.0, "ok" : 1 }
Nøgleværdiparret "ok":1 angiver, at operationen lykkedes, mens langsommere er den tærskeltid i millisekunder, en operation skal tage, og som standard er 100ms.
For at ændre denne værdi
db.setProfilingLevel(1, { slowms: 50 })
For at forespørge efter data mod system.profile-indsamlingen:
db.system.profile.find().pretty()
Brug af db.currentOp()helper
Denne funktion viser de aktuelle kørende forespørgsler med meget detaljerede oplysninger, såsom hvor længe de har kørt. På en løbende mongo-shell kører du kommentaren for eksempel:
db.currentOp({“secs_running”:{$gte:5}})
Hvor secs_running er filtreringsstrategien, så kun operationer, der har taget mere end 5 sekunder at udføre, returneres, hvilket reducerer outputtet. Dette bruges ofte, når CPU'ens helbred kan vurderes til 100 % på grund af den negative ydelsespåvirkning, det kan implicere på databasen. Så ved at ændre værdierne vil du lære, hvilke forespørgsler der tager lang tid at udføre.
De returnerede dokumenter har følgende som nøgler til interesse:
- forespørgsel :hvad forespørgslen indebærer
- aktiv : hvis forespørgslen stadig er i gang.
- ns :samlingsnavn, som forespørgslen skal udføres mod
- secs_running : varighed forespørgslen har taget indtil nu i sekunder
Ved at fremhæve, hvilke forespørgsler der tager lang tid, har du identificeret, hvad der overbelaster CPU'en.
Fortolkning af resultater og løsning af problemerne
Som vi har beskrevet ovenfor, er forespørgselsforsinkelse meget afhængig af mængden af involverede data, hvilket ellers vil føre til ineffektive eksekveringsplaner. Det vil sige, at hvis du for eksempel ikke bruger indekser i din samling og ønsker at opdatere visse poster, skal handlingen gennemgå alle dokumenter i stedet for kun at filtrere efter dem, der matcher forespørgselsspecifikationen. Logisk set vil dette tage længere tid, hvilket fører til en langsom forespørgsel. Du kan undersøge en ineffektiv eksekveringsplan ved at køre: explain(‘executionStats’) som giver statistik om forespørgslens ydeevne. Fra dette tidspunkt kan du lære, hvordan forespørgslen bruger indekset udover at give et fingerpeg om indekset er optimalt.
Hvis forklaringshjælperen vender tilbage
{
"queryPlanner" : {
"plannerVersion" : 1,
...
"winningPlan" : {
"stage" : "COLLSCAN",
...
}
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 3,
"executionTimeMillis" : 0,
"totalKeysExamined" : 0,
"totalDocsExamined" : 10,
"executionStages" : {
"stage" : "COLLSCAN",
...
},
...
},
...
}
queryPlanner.winningPlan.stage:COLLSCAN nøgleværdi angiver, at mongoden var nødt til at scanne hele samlingsdokumentet for at identificere resultaterne, og det bliver derfor en dyr operation, der fører til langsomme forespørgsler.
executionStats.totalKeysExamined:0 betyder, at samlingen ikke bruger indekseringsstrategi
For en given forespørgsel skal antallet af involverede dokumenter være tæt på nul. Hvis antallet af dokumenter er ret stort, er der to muligheder:
- Bruger ikke indeksering med samlingen
- Brug af et indeks, som ikke er optimalt.
Kør kommandoen for at oprette et indeks for en samling:
db.collection.createIndex( { quantity: 1 } )
Hvor kvantitet er et eksempelfelt, du har valgt for at være optimalt til indekseringsstrategien.
Hvis du vil vide mere om indeksering og hvilken indekseringsstrategi du skal bruge, så tjek denne blog
Konklusion
Forringelse af databasens ydeevne kan nemt skildres ved at have langsomme forespørgsler, hvilket er den mindste forventning, vi ønsker, at platformbrugere møder. Man kan identificere langsomme forespørgsler i MongoDB ved at aktivere profileren og konfigurere den til dens nogle specifikationer eller udføre db.currentOp() på en kørende mongod-instans.
Ved at se på tidsparametrene på det returnerede resultat kan vi identificere, hvilke forespørgsler der halter. Efter at have identificeret disse forespørgsler, bruger vi forklaringshjælperen på disse forespørgsler for at få flere detaljer, for eksempel hvis forespørgslen bruger et hvilket som helst indeks.
Uden indeksering bliver operationerne dyre, da mange dokumenter skal scannes igennem, før ændringerne anvendes. Med dette tilbageslag vil CPU'en blive overbelastet, hvilket resulterer i langsom forespørgsel og stigende CPU-spidser.
Den største fejl, der fører til langsomme forespørgsler, er ineffektiv eksekveringsplanlægning, som nemt kan løses ved at bruge et indeks med den involverede samling.