sql >> Database >  >> NoSQL >> MongoDB

Mangoeste bevolken vs object nesten

Het eerste dat u moet begrijpen over de populatie mangoesten, is dat het geen magie is, maar slechts een gemaksmethode waarmee u gerelateerde informatie kunt ophalen zonder het allemaal zelf te doen.

Het concept is in wezen bedoeld voor gebruik wanneer u besluit dat u gegevens in een afzonderlijke verzameling moet plaatsen in plaats van die gegevens in te sluiten, en uw belangrijkste overwegingen moeten doorgaans betrekking hebben op de documentgrootte of waar die gerelateerde informatie onderhevig is aan frequente updates waardoor ingebedde gegevens onpraktisch onderhouden.

Het "niet-magische" deel is dat in wezen wat er onder de dekens gebeurt, is dat wanneer u "verwijst" naar een andere bron, de vulfunctie een aanvullende vraag/vragen maakt naar die "gerelateerde" verzameling om die resultaten van de ouder te "samenvoegen" object dat u hebt opgehaald. Je zou dit zelf kunnen doen, maar de methode is er voor het gemak om de taak te vereenvoudigen. De voor de hand liggende "prestatie"-overweging is dat er geen enkele retourvlucht naar de database (MongoDB-instantie) is om alle informatie op te halen. Er is altijd meer dan één.

Neem als voorbeeld twee verzamelingen:

{ 
    "_id": ObjectId("5392fea00ff066b7d533a765"),
    "customerName": "Bill",
    "items": [
        ObjectId("5392fee10ff066b7d533a766"),
        ObjectId("5392fefe0ff066b7d533a767")
    ]
}

En de items:

{ "_id": ObjectId("5392fee10ff066b7d533a766"), "prod": "ABC", "qty": 1 }
{ "_id": ObjectId("5392fefe0ff066b7d533a767"), "prod": "XYZ", "qty": 2 }

Het "beste" dat kan worden gedaan door een "verwezen" model of het gebruik van populate (onder de motorkap) is dit:

var order = db.orders.findOne({ "_id": ObjectId("5392fea00ff066b7d533a765") });
order.items = db.items.find({ "_id": { "$in": order.items } ).toArray();

Er zijn dus duidelijk "minstens" twee zoekopdrachten en bewerkingen om die gegevens te "samenvoegen".

Het inbeddingsconcept is in wezen het MongoDB-antwoord op hoe om te gaan met het niet ondersteunen van "joins". Zodat u, in plaats van gegevens op te splitsen in genormaliseerde verzamelingen, de "gerelateerde" gegevens rechtstreeks probeert in te bedden in het document dat deze gebruikt. De voordelen hier zijn dat er een enkele "lees" -bewerking is voor het ophalen van de "gerelateerde" informatie, en ook een enkel punt van "schrijf" -bewerkingen om zowel de "ouder" als de "kind" -items bij te werken, hoewel het vaak niet mogelijk is om naar te schrijven "veel" kinderen tegelijk zonder "lijsten" op de client te verwerken of anderszins "meerdere" schrijfbewerkingen te accepteren, en bij voorkeur in "batch" -verwerking.

Gegevens zien er dan eerder zo uit (vergeleken met het bovenstaande voorbeeld):

{ 
    "_id": ObjectId("5392fea00ff066b7d533a765"),
    "customerName": "Bill",
    "items": [
        { "_id": ObjectId("5392fee10ff066b7d533a766"), "prod": "ABC", "qty": 1 },
        { "_id": ObjectId("5392fefe0ff066b7d533a767"), "prod": "XYZ", "qty": 2 }
    ]
}

Daarom is het daadwerkelijk ophalen van de gegevens slechts een kwestie van:

db.orders.findOne({ "_id": ObjectId("5392fea00ff066b7d533a765") });

De voor- en nadelen van beide zullen altijd grotendeels afhangen van het gebruikspatroon van uw toepassing. Maar in één oogopslag:

Inbedding

  • De totale documentgrootte met ingesloten gegevens zal doorgaans niet groter zijn dan 16 MB aan opslagruimte (de BSON-limiet) of anderszins (als richtlijn) arrays hebben die 500 of meer items bevatten.

  • Gegevens die zijn ingesloten, hoeven over het algemeen niet vaak te worden gewijzigd. Je zou dus kunnen leven met "duplicatie" die voortkomt uit de de-normalisatie die niet resulteert in de noodzaak om die "duplicaten" bij te werken met dezelfde informatie in veel bovenliggende documenten om alleen maar een wijziging aan te brengen.

  • Gerelateerde gegevens worden vaak gebruikt in samenwerking met de ouder. Wat betekent dat als uw "lees/schrijf"-gevallen vrijwel altijd moeten "lezen/schrijven" naar zowel ouder als kind, dan is het logisch om de gegevens voor atomaire bewerkingen in te sluiten.

Verwijzingen

  • De gerelateerde gegevens zullen altijd de 16 MB BSON-limiet overschrijden. Je kunt altijd een hybride benadering van "bucketing" overwegen, maar de algemene harde limiet van het hoofddocument kan niet worden overschreden. Veelvoorkomende gevallen zijn "post" en "comments" waarbij de activiteit "commentaar" naar verwachting erg groot zal zijn.

  • Gerelateerde gegevens moeten regelmatig worden bijgewerkt. Of in wezen het geval waarin u "normaliseert" omdat die gegevens door veel ouders worden "gedeeld" en de "gerelateerde" gegevens vaak genoeg worden gewijzigd dat het onpraktisch zou zijn om ingesloten items bij te werken in elke "ouder" waar dat "kind" -item voorkomt . Het gemakkelijkere geval is om gewoon naar het "kind" te verwijzen en de wijziging één keer aan te brengen.

  • Er is een duidelijke scheiding tussen lezen en schrijven. In het geval dat u misschien niet altijd die "gerelateerde" informatie nodig hebt bij het lezen van de "ouder" of anderszins niet altijd de "ouder" hoeft te veranderen wanneer u naar het kind schrijft, kan er een goede reden zijn om het model te scheiden zoals waarnaar wordt verwezen. Bovendien, als er een algemene wens is om veel "subdocumenten" tegelijk bij te werken waarin die "subdocumenten" eigenlijk verwijzingen zijn naar een andere verzameling, dan is de implementatie vaak efficiënter wanneer de gegevens zich in een aparte verzameling bevinden. collectie.

Er is dus eigenlijk een veel bredere discussie over de "voor- en nadelen" voor beide posities in de MongoDB-documentatie over gegevensmodellering, die verschillende gebruiksscenario's en manieren omvat om ofwel het insluiten of het model waarnaar wordt verwezen te benaderen, zoals wordt ondersteund door de populate-methode.

Hopelijk zijn de "puntjes" nuttig, maar de algemene aanbeveling is om rekening te houden met de gegevensgebruikspatronen van uw toepassing en te kiezen wat het beste is. Het hebben van de "optie" om "zou" in te sluiten, is de reden dat u voor MongoDB hebt gekozen, maar het is in feite hoe uw toepassing "de gegevens gebruikt" die de beslissing neemt welke methode past bij welk deel van uw gegevensmodellering (omdat het niet "alles of niets") het beste.

  1. Merk op dat aangezien dit oorspronkelijk was geschreven MongoDB de $lookup introduceerde operator die inderdaad "joins" uitvoert tussen verzamelingen op de server. Voor de doeleinden van de algemene discussie hier:"beter" in de meeste gevallen dan de "meervoudige zoekopdracht"-overhead die wordt veroorzaakt door populate() en "meerdere zoekopdrachten" in het algemeen, is er nog steeds een "aanzienlijke overhead" opgelopen met een $lookup operatie.

Het kernontwerpprincipe is "embedded" betekent "reeds aanwezig" in tegenstelling tot "ophalen van ergens anders". In wezen het verschil tussen "in je zak" en "op de plank", en in I/O-termen meestal meer als "op de plank in de bibliotheek in de binnenstad" , en met name verder weg voor netwerkgebaseerde verzoeken.




  1. MongoDB countDocuments()

  2. Big Data Processing Engines - Welke gebruik ik?:Deel 1

  3. Automatische MongoDB-back-up

  4. Mongoose limiet/offset en telling query