sql >> Database >  >> NoSQL >> MongoDB

Hoe de prestaties van MongoDB te optimaliseren?

Uitstekende databaseprestaties zijn belangrijk wanneer u toepassingen ontwikkelt met MongoDB. Soms kan het algehele proces voor gegevensverstrekking verslechteren vanwege een aantal redenen, waaronder:

  • Ongepaste schema-ontwerppatronen
  • Onjuist gebruik van of geen gebruik van indexeringsstrategieën
  • Ontoereikende hardware
  • Replicatievertraging
  • Slecht presterende zoektechnieken

Sommige van deze tegenslagen kunnen u dwingen om hardwarebronnen te vergroten, terwijl andere dat niet kunnen. Slechte query-structuren kunnen er bijvoorbeeld toe leiden dat de query lang duurt om te worden verwerkt, waardoor er vertraging in de replica optreedt en mogelijk zelfs gegevens verloren gaan. In dit geval zou je kunnen denken dat het opslaggeheugen misschien niet genoeg is en dat het waarschijnlijk moet worden opgeschaald. Dit artikel bespreekt de meest geschikte procedures die u kunt gebruiken om de prestaties van uw MongoDB-database te verbeteren.

Schemaontwerp

In principe zijn de twee meest gebruikte schemarelaties...

  • Een-op-enkele
  • Een-op-veel

Hoewel het meest efficiënte schemaontwerp de een-op-veel-relatie is, heeft elk schema zijn eigen voordelen en beperkingen.

Een-naar-enkele

In dit geval zijn er voor een bepaald veld ingesloten documenten, maar deze zijn niet geïndexeerd met objectidentiteit.

Hier is een eenvoudig voorbeeld:

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

Een voordeel van het gebruik van deze relatie is dat u de ingesloten documenten met slechts één query kunt ophalen. Vanuit het oogpunt van query's hebt u echter geen toegang tot een enkel ingesloten document. Dus als u niet afzonderlijk naar ingesloten documenten gaat verwijzen, is het optimaal om dit schema-ontwerp te gebruiken.

Een-op-veel

Voor deze relatie zijn gegevens in de ene database gerelateerd aan gegevens in een andere database. U kunt bijvoorbeeld een database hebben voor gebruikers en een andere voor berichten. Dus als een gebruiker een bericht plaatst, wordt dit geregistreerd met gebruikers-ID.

Gebruikersschema

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

Schema berichten

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

Het voordeel van dit schema-ontwerp is dat de documenten als standalone worden beschouwd (apart te selecteren). Een ander voordeel is dat dit ontwerp gebruikers van verschillende id's in staat stelt om informatie uit het posts-schema (vandaar de naam One-to-Many) te delen en soms een "N-to-N"-schema kan zijn - in principe zonder gebruik te maken van table join. De beperking van dit schema-ontwerp is dat u ten minste twee query's moet uitvoeren om gegevens in de tweede verzameling op te halen of te selecteren.

Hoe de gegevens moeten worden gemodelleerd, hangt daarom af van het toegangspatroon van de toepassing. Daarnaast moet u rekening houden met het schemaontwerp dat we hierboven hebben besproken.

Optimalisatietechnieken voor schemaontwerp

  1. Gebruik het insluiten van documenten zoveel mogelijk, aangezien dit het aantal query's dat u moet uitvoeren voor een bepaalde set gegevens, vermindert.

  2. Gebruik geen denormalisatie voor documenten die regelmatig worden bijgewerkt. Als anfield regelmatig wordt bijgewerkt, is het de taak om alle instanties te vinden die moeten worden bijgewerkt. Dit zal resulteren in een trage verwerking van zoekopdrachten, waardoor zelfs de voordelen van denormalisatie worden overweldigd.

  3. Als het nodig is om een ​​document afzonderlijk op te halen, is het niet nodig om insluiting te gebruiken, aangezien complexe query's, zoals geaggregeerde pipelining, meer tijd vergen om uit te voeren.

  4. Als de reeks documenten die moeten worden ingesloten groot genoeg is, sluit ze dan niet in. De array-groei moet op zijn minst een limiet hebben.

Goede indexering

Dit is het meest cruciale onderdeel van het afstemmen van de prestaties en vereist een uitgebreid begrip van de toepassingsquery's, de verhouding tussen lezen en schrijven en hoeveel vrij geheugen uw systeem heeft. Als u een index gebruikt, scant de query de index en niet de verzameling.

Een uitstekende index is er een die alle velden omvat die door een zoekopdracht zijn gescand. Dit wordt een samengestelde index genoemd.

Om een ​​enkele index voor een veld te maken, kunt u deze code gebruiken:

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

Voor een samengestelde index, om de indexering te maken:

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

Naast sneller opvragen door gebruik te maken van indexering, is er een bijkomend voordeel van andere bewerkingen zoals sorteren, samples en limit. Als ik mijn schema bijvoorbeeld ontwerp als {f:1, m:1}, kan ik een extra bewerking uitvoeren naast zoeken als

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

Het lezen van gegevens uit RAM is efficiënter dan het lezen van dezelfde gegevens van schijf. Om deze reden is het altijd aan te raden om ervoor te zorgen dat uw index volledig in het RAM-geheugen past. Om de huidige indexSize van uw verzameling te krijgen, voert u de opdracht uit:

db.collection.totalIndexSize()

U krijgt een waarde van 36864 bytes. Deze waarde mag ook geen groot percentage van de totale RAM-grootte in beslag nemen, aangezien u moet voorzien in de behoeften van de gehele werkset van de server.

Een efficiënte zoekopdracht moet ook de selectiviteit verbeteren. Selectiviteit kan worden gedefinieerd als het vermogen van een query om het resultaat te verfijnen met behulp van de index. Om secanter te zijn, moeten uw zoekopdrachten het aantal mogelijke documenten met het geïndexeerde veld beperken. Selectiviteit wordt meestal geassocieerd met een samengestelde index die een veld met lage selectiviteit en een ander veld omvat. Als u bijvoorbeeld over deze gegevens beschikt:

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

De zoekopdracht {a:7, b:“cd”} zal door 2 documenten scannen om 1 overeenkomend document terug te geven. Als de gegevens voor de waarde a echter gelijkmatig zijn verdeeld, d.w.z.

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

De query {a:7, b:“cd”} zal door 1 document scannen en dit document retourneren. Dit duurt dus korter dan de eerste datastructuur.

ClusterControlSingle Console voor uw gehele database-infrastructuurOntdek wat er nog meer nieuw is in ClusterControlInstalleer ClusterControl GRATIS

Resourcesvoorziening

Onvoldoende opslaggeheugen, RAM en andere bedrijfsparameters kunnen de prestaties van een MongoDB drastisch verminderen. Als het aantal gebruikersverbindingen bijvoorbeeld erg groot is, belemmert dit het vermogen van de servertoepassing om verzoeken tijdig af te handelen. Zoals besproken in Belangrijke dingen om te controleren in MongoDB, kunt u een overzicht krijgen van de beperkte middelen die u heeft en hoe u deze kunt schalen om aan uw specificaties te voldoen. Voor een groot aantal gelijktijdige aanvraagverzoeken zal het databasesysteem overweldigd worden om aan de vraag te voldoen.

Replicatievertraging

Soms merkt u dat er gegevens ontbreken in uw database of wanneer u iets verwijdert, verschijnt het opnieuw. Hoewel je een goed ontworpen schema, geschikte indexering en voldoende bronnen zou kunnen hebben, zal je applicatie in het begin soepel lopen zonder enige hapering, maar op een gegeven moment merk je de laatstgenoemde problemen op. MongoDB vertrouwt op een replicatieconcept waarbij gegevens redundant worden gekopieerd om aan een aantal ontwerpcriteria te voldoen. Een aanname hierbij is dat het proces instantaan is. Er kan echter enige vertraging optreden als gevolg van netwerkstoringen of onverwerkte fouten. Kortom, er zal een groot gat zitten tussen de tijd waarmee een bewerking wordt verwerkt op de primaire node en de tijd dat deze wordt uitgevoerd in de secundaire node.

Tegenslagen met replicavertragingen

  1. Inconsistente gegevens. Dit wordt vooral geassocieerd met leesbewerkingen die zijn verdeeld over secundairen.

  2. Als de lag gap groot genoeg is, bevinden zich mogelijk veel niet-gerepliceerde gegevens op het primaire knooppunt en moeten deze worden afgestemd op het secundaire knooppunt. Op een gegeven moment kan dit onmogelijk zijn, vooral wanneer het primaire knooppunt niet kan worden hersteld.

  3. Als het primaire knooppunt niet wordt hersteld, kan iemand worden gedwongen een knooppunt uit te voeren met gegevens die niet up-to-date zijn, waardoor de hele database kan worden verwijderd om het primaire knooppunt te herstellen.

Oorzaken van het falen van het secundaire knooppunt

  1. Overtreft het primaire vermogen ten opzichte van het secundaire met betrekking tot de specificaties van de CPU, schijf-IOPS en netwerk-I/O.

  2. Complexe schrijfbewerkingen. Bijvoorbeeld een commando als

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

    Het primaire knooppunt zal deze bewerking snel genoeg in de oplog opnemen. Voor het secundaire knooppunt moet het echter die ops ophalen, alle index- en gegevenspagina's in het RAM-geheugen inlezen om te voldoen aan een aantal criteriaspecificaties, zoals de id. Omdat het dit snel genoeg moet doen om de snelheid bij het primaire knooppunt te houden, doet het de bewerking, als het aantal ops groot genoeg is, zal er een verwachte vertraging zijn.

  3. Vergrendeling van de secundaire bij het maken van een back-up. In dit geval kunnen we vergeten de primaire uit te schakelen en zullen daarom doorgaan met zijn normale activiteiten. Op het moment dat de vergrendeling wordt vrijgegeven, zal de replicatievertraging groot zijn, vooral wanneer het gaat om een ​​enorme hoeveelheid gegevensback-up.

  4. Indexopbouw. Als een index zich opbouwt in het secundaire knooppunt, worden alle andere bijbehorende bewerkingen geblokkeerd. Als de index lang actief is, zal de hik van de replicatievertraging optreden.

  5. Niet aangesloten secundair. Soms kan het secundaire knooppunt mislukken als gevolg van verbroken netwerkverbindingen en dit resulteert in een replicatievertraging wanneer het opnieuw wordt verbonden.

Hoe de replicatievertraging te minimaliseren

  • Gebruik unieke indexen naast uw verzameling met het veld _id. Dit is om te voorkomen dat het replicatieproces volledig mislukt.

  • Overweeg andere soorten back-up, zoals momentopnames en momentopnamen van het bestandssysteem die niet per se vergrendeld hoeven te worden.

  • Vermijd het bouwen van grote indexen omdat deze de achtergrond blokkeren.

  • Maak de secundaire krachtig genoeg. Als de schrijfbewerking licht van gewicht is, is het gebruik van ondermaatse secundairen economisch. Maar voor zware schrijfbelastingen kan het secundaire knooppunt achterblijven bij het primaire. Om seculierer te zijn, moet de secundaire voldoende bandbreedte hebben om oplogs snel genoeg te kunnen lezen om de snelheid met de primaire node te behouden.

Efficiënte zoektechnieken

Naast het maken van geïndexeerde zoekopdrachten en het gebruik van Queryselectiviteit zoals hierboven besproken, zijn er andere concepten die u kunt gebruiken om uw zoekopdrachten vast te leggen en effectief te maken.

Uw zoekopdrachten optimaliseren

  1. Een gedekte zoekopdracht gebruiken. Een gedekte zoekopdracht is een zoekopdracht waaraan altijd volledig wordt voldaan door een index en hoeft daarom geen enkel document te onderzoeken. De gedekte zoekopdracht zou daarom alle velden als onderdeel van de index moeten hebben en bijgevolg zou het resultaat al deze velden moeten bevatten.

    Laten we eens kijken naar dit voorbeeld:

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

    Als we een index voor deze collectie maken als

    {“product.price”: 1} 

    Als een zoekbewerking wordt overwogen, zal deze index deze zoekopdracht dekken;

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

    en retourneer alleen het veld product.price en waarde.

  2. Gebruik voor ingesloten documenten de puntnotatie (.). De puntnotatie helpt bij het verkrijgen van toegang tot elementen van een array en velden van ingesloten documenten.

    Toegang tot een array:

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

    Om bijvoorbeeld het vierde element te specificeren, kun je dit commando schrijven:

    “prices.3”

    Toegang tot een objectarray:

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

    Om het naamveld in de voertuigenreeks te specificeren, kunt u dit commando gebruiken

    “vehicles.name”
  3. Controleer of een vraag wordt gedekt. Gebruik hiervoor de db.collection.explain(). Deze functie geeft informatie over de uitvoering van andere bewerkingen -bijv. db.collection.explain().aggregate(). Voor meer informatie over de functie explain kun je explain() bekijken.

Over het algemeen is de beste techniek voor het opvragen het gebruik van indexen. Het opvragen van alleen een index is veel sneller dan het opvragen van documenten buiten de index. Ze passen in het geheugen en zijn daarom beschikbaar in RAM in plaats van op schijf. Dit maakt het gemakkelijk en snel genoeg om ze uit het geheugen op te halen.


  1. MongoDB $ceil

  2. redis:teller elke dag resetten

  3. hoe je onderliggende objecten in mongodb kunt opvragen

  4. Hoe gegevens in Model op te maken voordat u ze opslaat in Mongoose (ExpressJS)