sql >> Databasteknik >  >> NoSQL >> MongoDB

Hur man optimerar prestanda för MongoDB

Utmärkt databasprestanda är viktigt när du utvecklar applikationer med MongoDB. Ibland kan den övergripande databetjäningsprocessen försämras på grund av ett antal orsaker, av vilka några inkluderar:

  • Olämpliga mönster för schemadesign
  • Felaktig användning av eller ingen användning av indexeringsstrategier
  • Otillräcklig hårdvara
  • Replikeringsfördröjning
  • Frågetekniker som fungerar dåligt

Vissa av dessa motgångar kan tvinga dig att öka hårdvaruresurserna medan andra kanske inte. Till exempel kan dåliga frågestrukturer leda till att frågan tar lång tid att bearbetas, vilket orsakar replikfördröjning och kanske till och med viss dataförlust. I det här fallet kan man tro att lagringsminnet kanske inte räcker till, och att det förmodligen behöver skalas upp. Den här artikeln diskuterar de mest lämpliga procedurerna du kan använda för att öka prestandan för din MongoDB-databas.

Schemadesign

I grund och botten är de två vanligaste schemarelationerna...

  • En-till-få
  • En-till-många

Även om den mest effektiva schemadesignen är One-to-Many-relationen, har var och en sina egna fördelar och begränsningar.

En-till-få

I det här fallet, för ett givet fält, finns det inbäddade dokument men de är inte indexerade med objektidentitet.

Här är ett enkelt exempel:

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

En fördel med att använda denna relation är att du kan få de inbäddade dokumenten med bara en enda fråga. Men ur frågesynpunkt kan du inte komma åt ett enda inbäddat dokument. Så om du inte ska referera till inbäddade dokument separat, kommer det att vara optimalt att använda denna schemadesign.

En-till-många

För detta förhållande är data i en databas relaterad till data i en annan databas. Du kan till exempel ha en databas för användare och en annan för inlägg. Så om en användare gör ett inlägg registreras det med användar-id.

Användarschema

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

Inläggsschema

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

Fördelen med denna schemadesign är att dokumenten betraktas som fristående (kan väljas separat). En annan fördel är att den här designen gör det möjligt för användare av olika id att dela information från inläggsschemat (därav namnet One-to-Many) och ibland kan det vara "N-to-N"-schema - i princip utan att använda table join. Begränsningen med denna schemadesign är att du måste göra minst två frågor för att hämta eller välja data i den andra samlingen.

Hur man modellerar data beror därför på applikationens åtkomstmönster. Utöver detta måste du överväga schemadesignen som vi har diskuterat ovan.

Optimeringstekniker för schemadesign

  1. Använd dokumentinbäddning så mycket som möjligt eftersom det minskar antalet frågor du behöver köra för en viss uppsättning data.

  2. Använd inte denormalisering för dokument som uppdateras ofta. Om anfield kommer att uppdateras ofta, kommer det att finnas uppgiften att hitta alla instanser som behöver uppdateras. Detta kommer att resultera i långsam frågebearbetning, och därmed överväldigande även fördelarna som är förknippade med denormalisering.

  3. Om det finns ett behov av att hämta ett dokument separat, så finns det inget behov av att använda inbäddning eftersom komplexa frågor som aggregerad pipelining tar längre tid att köra.

  4. Om utbudet av dokument som ska bäddas in är tillräckligt stort, bädda inte in dem. Arraytillväxten bör åtminstone ha en bunden gräns.

Korrekt indexering

Detta är den mer kritiska delen av prestandajustering och kräver att man har en omfattande förståelse för applikationsfrågor, förhållandet mellan läsning och skrivning och hur mycket ledigt minne ditt system har. Om du använder ett index kommer frågan att skanna indexet och inte samlingen.

Ett utmärkt index är ett som involverar alla fält som skannas av en fråga. Detta kallas ett sammansatt index.

För att skapa ett enda index för ett fält kan du använda denna kod:

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

För ett sammansatt index, för att skapa indexeringen:

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

Förutom snabbare sökning med hjälp av indexering, finns det en ytterligare fördel med andra operationer som sortering, sampel och limit. Till exempel, om jag designar mitt schema som {f:1, m:1} kan jag göra en extra operation förutom hitta som

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

Att läsa data från RAM är effektivare än att läsa samma data från disk. Av denna anledning rekommenderas det alltid att se till att ditt index passar helt och hållet i RAM-minnet. För att få den aktuella indexSize för din samling, kör kommandot :

db.collection.totalIndexSize()

Du kommer att få ett värde som 36864 byte. Detta värde bör inte heller ta en stor procentandel av den totala RAM-storleken, eftersom du måste tillgodose behoven hos hela serverns arbetsuppsättning.

En effektiv fråga bör också förbättra selektiviteten. Selektivitet kan definieras som en frågas förmåga att begränsa resultatet med hjälp av index. För att vara mer sekant bör dina frågor begränsa antalet möjliga dokument med det indexerade fältet. Selektivitet är mestadels förknippat med ett sammansatt index som inkluderar ett fält med låg selektivitet och ett annat fält. Till exempel om du har dessa uppgifter:

{ _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 }

Frågan {a:7, b:“cd”} kommer att skanna igenom 2 dokument för att returnera 1 matchande dokument. Men om data för värdet a är jämnt fördelade, 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 }

Frågan {a:7, b:“cd”} kommer att skanna igenom 1 dokument och returnera detta dokument. Detta kommer därför att ta kortare tid än den första datastrukturen.

ClusterControlSingle Console för hela din databasinfrastrukturTa reda på vad mer som är nytt i ClusterControlInstallera ClusterControl GRATIS

Resursförsörjning

Otillräckligt lagringsminne, RAM och andra driftsparametrar kan drastiskt försämra prestandan hos en MongoDB. Till exempel, om antalet användaranslutningar är mycket stort, kommer det att hindra serverapplikationens förmåga att hantera förfrågningar i rätt tid. Som diskuterats i Viktiga saker att övervaka i MongoDB kan du få en överblick över vilka begränsade resurser du har och hur du kan skala dem för att passa dina specifikationer. För ett stort antal samtidiga applikationsförfrågningar kommer databassystemet att överväldigas i takt med efterfrågan.

Replikeringsfördröjning

Ibland kanske du märker att vissa data saknas i din databas eller när du tar bort något visas det igen. Så mycket som du kan ha ett väldesignat schema, lämplig indexering och tillräckligt med resurser, i början kommer din applikation att fungera smidigt utan några problem, men sedan någon gång märker du de sistnämnda problemen. MongoDB förlitar sig på replikeringskoncept där data kopieras redundant för att uppfylla vissa designkriterier. Ett antagande med detta är att processen är momentan. Vissa förseningar kan dock uppstå på grund av nätverksfel eller obehandlade fel. I ett nötskal kommer det att finnas ett stort gap mellan den tid med vilken en operation bearbetas på den primära noden och den tid den kommer att tillämpas i den sekundära noden.

Föregångar med replikfördröjningar

  1. Inkonsekventa data. Detta är särskilt förknippat med läsoperationer som är fördelade över sekundärer.

  2. Om fördröjningsgapet är tillräckligt stort, kan mycket oreplikerad data finnas på den primära noden och kommer att behöva stämmas av i den sekundära noden. Vid någon tidpunkt kan detta vara omöjligt, särskilt när den primära noden inte kan återställas.

  3. Om man misslyckas med att återställa den primära noden kan man tvinga en att köra en nod med data som inte är uppdaterad och kan följaktligen tappa hela databasen för att få den primära att återställa.

Orsaker till det sekundära nodfelet

  1. Överträffar primärkraften över den sekundära när det gäller CPU, disk IOPS och nätverks I/O-specifikationer.

  2. Komplexa skrivoperationer. Till exempel ett kommando som

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

    Den primära noden kommer att registrera denna operation i oploggen tillräckligt snabbt. Men för den sekundära noden måste den hämta dessa operationer, läsa in alla index- och datasidor i RAM-minnet för att uppfylla vissa kriteriespecifikationer såsom id. Eftersom det måste göra detta tillräckligt snabbt för att hålla hastigheten med den primära noden utför operationen, om antalet operationer är tillräckligt stort kommer det att finnas en förväntad fördröjning.

  3. Låsning av sekundären vid säkerhetskopiering. I det här fallet kan vi glömma att inaktivera den primära och kommer därför att fortsätta med dess verksamhet som vanligt. Vid den tidpunkt då låset kommer att släppas kommer replikeringsfördröjningen att ha ett stort gap, särskilt när man hanterar en enorm mängd datasäkerhetskopiering.

  4. Indexbyggnad. Om ett index byggs upp i den sekundära noden, blockeras alla andra operationer som är associerade med den. Om indexet är långvarigt kommer replikeringsfördröjningen att uppstå.

  5. Oansluten sekundär. Ibland kan den sekundära noden misslyckas på grund av nätverksavbrott och detta resulterar i en replikeringsfördröjning när den återansluts.

Hur man minimerar replikeringsfördröjningen

  • Använd unika index förutom att din samling har fältet _id. Detta för att undvika att replikeringsprocessen misslyckas helt.

  • Överväg andra typer av säkerhetskopiering som punkt-i-tid och filsystem ögonblicksbilder som inte nödvändigtvis kräver låsning.

  • Undvik att bygga stora index eftersom de orsakar bakgrundsblockering.

  • Gör sekundären tillräckligt kraftfull. Om skrivoperationen är lätt, kommer det att vara ekonomiskt att använda underdrivna sekundärer. Men för tunga skrivbelastningar kan den sekundära noden släpa efter den primära. För att vara mer seccant bör den sekundära ha tillräckligt med bandbredd för att hjälpa till att läsa oploggar tillräckligt snabbt för att hålla sin hastighet med den primära noden.

Effektiva frågetekniker

Förutom att skapa indexerade frågor och använda frågeselektivitet som diskuterats ovan, finns det andra koncept som du kan använda för att fästa och göra dina frågor effektiva.

Optimera dina frågor

  1. Använder en täckt fråga. En täckt fråga är en som alltid är helt tillfredsställd av ett index och behöver därför inte granska något dokument. Den täckta frågan bör därför ha alla fält som en del av indexet och följaktligen bör resultatet innehålla alla dessa fält.

    Låt oss betrakta detta exempel:

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

    Om vi ​​skapar ett index för denna samling som

    {“product.price”: 1} 

    Med tanke på en sökoperation kommer detta index att täcka denna fråga;

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

    och returnera endast fältet product.price och värde.

  2. För inbäddade dokument, använd punktnotationen (.). Punktnotationen hjälper till att komma åt element i en array och fält av inbäddade dokument.

    Åtkomst till en array:

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

    För att till exempel ange det fjärde elementet kan du skriva detta kommando:

    “prices.3”

    Åtkomst till en objektmatris:

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

    För att ange namnfältet i fordonsarrayen kan du använda detta kommando

    “vehicles.name”
  3. Kontrollera om en fråga är täckt. För att göra detta använd db.collection.explain(). Denna funktion kommer att ge information om utförandet av andra operationer -t.ex. db.collection.explain().aggregate(). För att lära dig mer om förklara-funktionen kan du kolla in explain().

I allmänhet är den överlägsna tekniken när det gäller frågor att använda index. Att bara fråga ett index är mycket snabbare än att fråga efter dokument utanför indexet. De kan passa i minnet och är därför tillgängliga i RAM snarare än i disk. Detta gör det enkelt och snabbt nog att hämta dem från minnet.


  1. Transaktioner och bevakningsutdrag i Redis

  2. Flytta underfält till toppnivå i projektion utan att lista alla nycklar

  3. Mongodb count distinkt med flera gruppfält

  4. Hur får jag Redis att köra på Azure?