sql >> Database teknologi >  >> NoSQL >> MongoDB

Sådan optimeres ydeevnen af ​​MongoDB

Fremragende databaseydeevne er vigtig, når du udvikler applikationer med MongoDB. Nogle gange kan den overordnede dataserveringsproces blive forringet på grund af en række årsager, hvoraf nogle omfatter:

  • Upassende skemadesignmønstre
  • Ukorrekt brug af eller ingen brug af indekseringsstrategier
  • Utilstrækkelig hardware
  • Replikeringsforsinkelse
  • Dårligt ydende forespørgselsteknikker

Nogle af disse tilbageslag kan tvinge dig til at øge hardwareressourcerne, mens andre måske ikke. For eksempel kan dårlige forespørgselsstrukturer resultere i, at forespørgslen tager lang tid at blive behandlet, hvilket forårsager replikaforsinkelse og måske endda noget datatab. I dette tilfælde kan man tænke, at lagerhukommelsen måske ikke er nok, og at den sandsynligvis trænger til at blive opskaleret. Denne artikel diskuterer de mest passende procedurer, du kan bruge til at øge ydeevnen af ​​din MongoDB-database.

Skemadesign

Grundlæggende er de to mest almindeligt anvendte skemarelationer...

  • En-til-få
  • En-til-mange

Mens det mest effektive skemadesign er One-to-Many-forholdet, har hver deres egne fordele og begrænsninger.

En-til-få

I dette tilfælde, for et givet felt, er der indlejrede dokumenter, men de er ikke indekseret med objektidentitet.

Her er et simpelt eksempel:

{
      userName: "Brian Henry",
      Email : "[email protected]",
      grades: [
             {subject: ‘Mathematics’,  grade: ‘A’},
             {subject: English,  grade: ‘B’},
      ]
}

En fordel ved at bruge dette forhold er, at du kan få de indlejrede dokumenter med kun en enkelt forespørgsel. Men fra et forespørgende synspunkt kan du ikke få adgang til et enkelt integreret dokument. Så hvis du ikke skal referere til indlejrede dokumenter separat, vil det være optimalt at bruge dette skemadesign.

En-til-mange

For dette forhold er data i en database relateret til data i en anden database. For eksempel kan du have en database for brugere og en anden for indlæg. Så hvis en bruger laver et indlæg bliver det registreret med bruger-id.

Brugerskema

{ 
    Full_name: “John Doh”,
    User_id: 1518787459607.0
}

Indlægsskema

{
    "_id" : ObjectId("5aa136f0789cf124388c1955"),
    "postTime" : "16:13",
    "postDate" : "8/3/2018",
    "postOwnerNames" : "John Doh",
    "postOwner" : 1518787459607.0,
    "postId" : "1520514800139"
}

Fordelen ved dette skemadesign er, at dokumenterne betragtes som selvstændige (kan vælges separat). En anden fordel er, at dette design gør det muligt for brugere af forskellige id'er at dele information fra posts-skemaet (deraf navnet One-to-Many) og nogle gange kan være "N-to-N"-skema - dybest set uden at bruge table join. Begrænsningen med dette skemadesign er, at du skal lave mindst to forespørgsler for at hente eller vælge data i den anden samling.

Hvordan man modellerer dataene vil derfor afhænge af applikationens adgangsmønster. Udover dette skal du overveje skemadesignet, vi har diskuteret ovenfor.

Optimeringsteknikker til skemadesign

  1. Brug dokumentindlejring så meget som muligt, da det reducerer antallet af forespørgsler, du skal køre for et bestemt sæt data.

  2. Brug ikke denormalisering til dokumenter, der ofte opdateres. Hvis anfield skal opdateres hyppigt, så vil der være opgaven med at finde alle de instanser, der skal opdateres. Dette vil resultere i langsom forespørgselsbehandling, og dermed overvældende selv fordelene forbundet med denormalisering.

  3. Hvis der er behov for at hente et dokument separat, er der ingen grund til at bruge indlejring, da komplekse forespørgsler såsom aggregeret pipelining tager længere tid at udføre.

  4. Hvis rækken af ​​dokumenter, der skal indlejres, er stor nok, skal du ikke integrere dem. Array-væksten bør i det mindste have en bundet grænse.

Korrekt indeksering

Dette er den mere kritiske del af ydeevnejustering og kræver, at man har en omfattende forståelse af applikationsforespørgslerne, forholdet mellem læsning og skrivning og hvor meget ledig hukommelse dit system har. Hvis du bruger et indeks, vil forespørgslen scanne indekset og ikke samlingen.

Et fremragende indeks er et, der involverer alle de felter, der scannes af en forespørgsel. Dette omtales som et sammensat indeks.

For at oprette et enkelt indeks for et felt kan du bruge denne kode:

db.collection.createIndex({“fields”: 1})

For et sammensat indeks, for at oprette indekseringen:

db.collection.createIndex({“filed1”: 1, “field2”:  1})

Udover hurtigere forespørgsel ved brug af indeksering, er der en yderligere fordel ved andre operationer såsom sortering, samples og limit. Hvis jeg f.eks. designer mit skema som {f:1, m:1}, kan jeg udføre en ekstra handling udover find as

db.collection.find( {f: 1} ).sort( {m: 1} )

Læsning af data fra RAM er mere effektiv end at læse de samme data fra disk. Af denne grund tilrådes det altid at sikre, at dit indeks passer helt ind i RAM'en. For at få den aktuelle indexSize for din samling skal du køre kommandoen :

db.collection.totalIndexSize()

Du får en værdi som 36864 bytes. Denne værdi bør heller ikke tage en stor procentdel af den samlede RAM-størrelse, da du skal imødekomme behovene for hele serverens arbejdssæt.

En effektiv forespørgsel bør også øge selektiviteten. Selektivitet kan defineres som en forespørgsels evne til at indsnævre resultatet ved hjælp af indekset. For at være mere sekant, bør dine forespørgsler begrænse antallet af mulige dokumenter med det indekserede felt. Selektivitet er for det meste forbundet med et sammensat indeks, som inkluderer et felt med lav selektivitet og et andet felt. For eksempel hvis du har disse data:

{ _id: ObjectId(), a: 6, b: "no", c: 45 }
{ _id: ObjectId(), a: 7, b: "gh", c: 28 }
{ _id: ObjectId(), a: 7, b: "cd", c: 58 }
{ _id: ObjectId(), a: 8, b: "kt", c: 33 }

Forespørgslen {a:7, b:"cd"} vil scanne gennem 2 dokumenter for at returnere 1 matchende dokument. Men hvis dataene for værdien a er jævnt fordelt, dvs.

{ _id: ObjectId(), a: 6, b: "no", c: 45 }
{ _id: ObjectId(), a: 7, b: "gh", c: 28 }
{ _id: ObjectId(), a: 8, b: "cd", c: 58 }
{ _id: ObjectId(), a: 9, b: "kt", c: 33 }

Forespørgslen {a:7, b:"cd"} vil scanne gennem 1 dokument og returnere dette dokument. Dette vil derfor tage kortere tid end den første datastruktur.

ClusterControlSingle Console for hele din databaseinfrastrukturFind ud af, hvad der ellers er nyt i ClusterControlInstaller ClusterControl GRATIS

Ressourceforsyning

Utilstrækkelig lagerhukommelse, RAM og andre driftsparametre kan drastisk forringe ydeevnen af ​​en MongoDB. For eksempel, hvis antallet af brugerforbindelser er meget stort, vil det forhindre serverapplikationens evne til at håndtere anmodninger rettidigt. Som diskuteret i Key things to monitor in MongoDB, kan du få et overblik over, hvilke begrænsede ressourcer du har, og hvordan du kan skalere dem, så de passer til dine specifikationer. For et stort antal samtidige ansøgningsanmodninger vil databasesystemet blive overvældet i forhold til efterspørgslen.

Replikeringsforsinkelse

Nogle gange kan du bemærke, at nogle data mangler fra din database, eller når du sletter noget, dukker det op igen. Så meget som du kunne have et veldesignet skema, passende indeksering og tilstrækkelige ressourcer, i begyndelsen vil din applikation køre problemfrit uden nogen problemer, men så på et tidspunkt bemærker du de sidstnævnte problemer. MongoDB er afhængig af replikeringskoncept, hvor data kopieres redundant for at opfylde nogle designkriterier. En antagelse med dette er, at processen er øjeblikkelig. Der kan dog forekomme en vis forsinkelse, muligvis på grund af netværksfejl eller uhåndterede fejl. Kort fortalt vil der være et stort mellemrum mellem den tid, hvormed en operation behandles på den primære node, og den tid, den vil blive anvendt i den sekundære node.

Tilbagegange med replikaforsinkelser

  1. Inkonsistente data. Dette er især forbundet med læseoperationer, der er fordelt på tværs af sekundære.

  2. Hvis lag-gabet er stort nok, kan en masse u-replikerede data være på den primære node og skal afstemmes i den sekundære node. På et tidspunkt kan dette være umuligt, især når den primære node ikke kan gendannes.

  3. Manglende retablering af den primære node kan tvinge en til at køre en node med data, som ikke er opdateret, og kan derfor droppe hele databasen for at få den primære til at gendanne.

Årsager til den sekundære nodefejl

  1. Udkonkurrerende primær magt over den sekundære med hensyn til CPU, disk IOPS og netværk I/O specifikationer.

  2. Komplekse skriveoperationer. For eksempel en kommando som

    db.collection.update( { a: 7}  , {$set: {m: 4} }, {multi: true} )

    Den primære node vil registrere denne operation i oploggen hurtigt nok. For den sekundære node skal den dog hente disse operationer, læse ethvert indeks og datasider ind i RAM for at opfylde nogle kriterier, såsom id'et. Da det skal gøre dette hurtigt nok for at holde hastigheden med den primære knude, udfører operationen, hvis antallet af operationer er stort nok, vil der være en forventet forsinkelse.

  3. Låsning af sekundæren ved sikkerhedskopiering. I dette tilfælde kan vi glemme at deaktivere den primære, og vil derfor fortsætte med dens drift som normalt. På det tidspunkt, hvor låsen frigives, vil replikeringsforsinkelsen have været stor, især når der er tale om en enorm mængde datasikkerhedskopiering.

  4. Indeksbygning. Hvis et indeks opbygges i den sekundære node, så blokeres alle andre operationer forbundet med det. Hvis indekset er langvarigt, vil replikationsforsinkelsen blive stødt på.

  5. Ikke-forbundet sekundær. Nogle gange kan den sekundære node svigte på grund af netværksafbrydelser, og dette resulterer i en replikeringsforsinkelse, når den genoprettes.

Sådan minimeres replikeringsforsinkelsen

  • Brug unikke indekser udover at din samling har feltet _id. Dette er for at undgå, at replikeringsprocessen mislykkes fuldstændigt.

  • Overvej andre typer sikkerhedskopiering, såsom tidspunkter og snapshots af filsystemet, som ikke nødvendigvis kræver låsning.

  • Undgå at bygge store indekser, da de forårsager baggrundsblokering.

  • Gør det sekundære kraftigt nok. Hvis skriveoperationen er letvægts, vil det være økonomisk at bruge underdrevne sekundære. Men for store skrivebelastninger kan den sekundære node halte bagud den primære. For at være mere seccant bør den sekundære have nok båndbredde til at hjælpe med at læse oplogs hurtigt nok til at holde sin hastighed med den primære node.

Effektive forespørgselsteknikker

Udover at oprette indekserede forespørgsler og bruge forespørgselsselektivitet som beskrevet ovenfor, er der andre koncepter, du kan bruge til at fastgøre og gøre dine forespørgsler effektive.

Optimering af dine forespørgsler

  1. Brug af en dækket forespørgsel. En dækket forespørgsel er en forespørgsel, der altid er fuldstændig tilfredsstillet af et indeks, og behøver derfor ikke at undersøge noget dokument. Den dækkede forespørgsel bør derfor have alle felter som en del af indekset, og resultatet bør derfor indeholde alle disse felter.

    Lad os overveje dette eksempel:

    {_id: 1, product: { price: 50 }

    Hvis vi opretter et indeks for denne samling som

    {“product.price”: 1} 

    I betragtning af en find-operation vil dette indeks dække denne forespørgsel;

    db.collection.find( {“product.price”: 50}, {“product.price”: 1, _id: 0}  )

    og returner kun feltet product.price og værdi.

  2. For indlejrede dokumenter skal du bruge punktnotationen (.). Punktnotationen hjælper med at få adgang til elementer i et array og felter i det indlejrede dokument.

    Adgang til et array:

    {
       prices: [12, 40, 100, 50, 40]  
    }

    For at angive det fjerde element for eksempel, kan du skrive denne kommando:

    “prices.3”

    Adgang til et objektarray:

    {
    
       vehicles: [{name: toyota, quantity: 50},
                 {name: bmw, quantity: 100},
                 {name: subaru, quantity: 300}                    
    } 

    For at angive navnefeltet i køretøjsarrayet kan du bruge denne kommando

    “vehicles.name”
  3. Tjek, om en forespørgsel er dækket. For at gøre dette skal du bruge filen db.collection.explain(). Denne funktion vil give information om udførelsen af ​​andre operationer -f.eks. db.collection.explain().aggregate(). For at lære mere om forklaringsfunktionen kan du tjekke explain().

Generelt er den øverste teknik, hvad angår forespørgsler, at bruge indekser. At forespørge kun et indeks er meget hurtigere end at forespørge på dokumenter uden for indekset. De kan passe i hukommelsen og derfor tilgængelige i RAM i stedet for på disk. Dette gør det nemt og hurtigt nok til at hente dem fra hukommelsen.


  1. Understøtter Spring Data Redis (1.3.2.RELEASE) JedisSentinelPool of jedis?

  2. Redis som meddelelsesmægler

  3. implementerer redis til heroku ude af stand til at oprette forbindelse

  4. Sådan søger du efter indholdsværdi i redis by BookSleeve