Scegliere il Giusto Modello di Dati MongoDB: Documenti Incorporati vs. Documenti Referenziati
Scegli documenti MongoDB incorporati o referenziati in base ai pattern di accesso, alla crescita, alla coerenza e al comportamento degli aggiornamenti.
Scegliere il Modello Dati MongoDB Corretto: Documenti Incorporati vs. Documenti Referenziati
La modellazione dei dati in MongoDB solitamente si riduce a una domanda pratica: i dati correlati dovrebbero vivere all'interno dello stesso documento, o un documento dovrebbe referenziarne un altro? Questa scelta influisce sulla velocità di lettura, sul costo degli aggiornamenti, sulla crescita del documento e su quanto lavoro di coerenza la tua applicazione deve gestire.
Questa guida approfondisce i compromessi tra l'incorporamento di documenti all'interno di un documento padre e il riferimento a documenti correlati in diverse collezioni. Comprendere quando e come applicare queste tecniche ti permetterà di progettare schemi MongoDB efficienti e ad alte prestazioni, su misura per i pattern di accesso specifici della tua applicazione.
Comprendere le Strategie di Modellazione dei Dati MongoDB
MongoDB organizza i dati in documenti (simili a oggetti JSON) memorizzati in collezioni. Le relazioni tra questi documenti possono essere modellate utilizzando due pattern principali:
- Incorporamento (Denormalizzazione): Memorizzare i dati correlati direttamente all'interno del documento padre.
- Riferimento (Normalizzazione): Memorizzare solo un riferimento (come un
_id) al documento correlato in un'altra collezione, simile a una chiave esterna.
1. Il Pattern di Incorporamento (Denormalizzazione)
L'incorporamento consiste nel posizionare un documento direttamente all'interno di un altro. Questa tecnica è molto favorita in MongoDB quando le relazioni dei dati sono uno-a-pochi o quando i dati correlati vengono acceduti frequentemente insieme al documento padre.
Quando Usare l'Incorporamento
Usa il pattern di incorporamento quando:
- I dati vengono acceduti insieme: Se hai quasi sempre bisogno dei dati correlati quando interroghi il padre, l'incorporamento minimizza il numero di operazioni sul database necessarie per recuperare il set completo di informazioni.
- Relazioni Uno-a-Pochi: Ideale per relazioni in cui l'array di documenti incorporati rimane relativamente piccolo e prevedibile (ad esempio, le ultime 10 attività di accesso di un utente o gli articoli di un ordine).
- La Coerenza dei Dati è Critica: I dati incorporati sono intrinsecamente coerenti perché risiedono all'interno di un singolo documento, semplificando le garanzie di atomicità fornite dalle transazioni ACID su singolo documento di MongoDB.
Esempio di Incorporamento
Considera un Prodotto e le sue Recensioni. Se le recensioni vengono spesso recuperate con il prodotto e il numero totale di recensioni è gestibile:
// Documento della Collezione Prodotto
{
"_id": ObjectId("..."),
"nome": "SSD ad Alte Prestazioni",
"prezzo": 129.99,
"recensioni": [
{
"utente": "Alice",
"valutazione": 5,
"commento": "L'unità più veloce di sempre!"
},
{
"utente": "Bob",
"valutazione": 4,
"commento": "Ottimo rapporto qualità-prezzo."
}
]
}
Svantaggi dell'Incorporamento
- Limiti di Dimensione del Documento: I documenti MongoDB hanno un limite di dimensione massima di 16MB. Se l'array di documenti incorporati cresce senza un limite chiaro, potresti aver bisogno di riferimenti o bucketing.
- Overhead di Aggiornamento: Grandi array incorporati possono rendere gli aggiornamenti più costosi e aumentare la contesa sul documento padre.
- Duplicazione dei Dati: Se i dati incorporati devono essere condivisi o visualizzati indipendentemente dal padre, rischi la duplicazione dei dati e problemi di coerenza eventuale se gli aggiornamenti non vengono sincronizzati su tutte le copie.
2. Il Pattern di Riferimento (Normalizzazione)
Il riferimento imita il concetto di chiavi esterne nei database relazionali. Invece di incorporare i dati correlati, memorizzi l'_id o un altro identificatore stabile per il documento correlato. Questo richiede spesso una seconda query, una fase di aggregazione $lookup o un join lato applicazione per recuperare i dati correlati.
Quando Usare il Riferimento
Usa il pattern di riferimento quando:
- Relazioni Uno-a-Molti o Molti-a-Molti: Quando un lato della relazione può crescere indefinitamente (ad esempio, il numero di commenti su un post del blog o utenti che appartengono a molti gruppi).
- Dati Condivisi tra Più Padri: Se l'entità dati correlata deve essere aggiornata e acceduta indipendentemente da molti altri documenti (ad esempio, un documento
Categoriausato da molti documentiProdotto). - Set di Dati Grandi: Quando l'incorporamento violerebbe il limite di dimensione del documento di 16MB.
Tipi di Riferimento
A. Riferimenti Manuali (Join Lato Applicazione)
Memorizzare l'_id nel documento padre:
// Collezione Autore
{
"_id": ObjectId("autore123"),
"nome": "Mario Rossi"
}
// Collezione Libro
{
"_id": ObjectId("libro456"),
"titolo": "Modellazione Dati 101",
"autore_id": ObjectId("autore123") // Riferimento
}
Per recuperare il nome dell'autore, esegui due query o usa $lookup:
// Esempio usando $lookup nel framework di aggregazione
db.libri.aggregate([
{ $match: { titolo: "Modellazione Dati 101" } },
{
$lookup: {
from: "autori", // Collezione da unire
localField: "autore_id", // Campo dai documenti di input (libri)
foreignField: "_id", // Campo dai documenti della collezione 'from' (autori)
as: "dettagli_autore"
}
}
]);
B. Riferimenti Bidirezionali
Per relazioni bidirezionali, puoi anche referenziare il padre nel documento figlio. Questo rende più facile attraversare la relazione in entrambe le direzioni, sebbene aumenti l'overhead di scrittura poiché gli aggiornamenti devono avvenire in due punti.
Svantaggi del Riferimento
- Maggiore Complessità delle Query: Recuperare dati completamente denormalizzati richiede join (tramite codice applicativo o
$lookupdi MongoDB), che possono essere più lenti di una singola operazione di lettura incorporata. - Gestione della Coerenza: Se duplichi campi selezionati da un documento referenziato, come il nome visualizzato di un autore su un record di libro, devi aggiornare quelle copie o accettare una temporanea obsolescenza.
Riepilogo: Fare la Scelta Giusta
La decisione tra incorporamento e riferimento ruota attorno ai pattern di accesso. Chiediti: Quanto spesso vengono recuperati questi dati correlati? Quanto spesso cambiano? Sono piccoli o potenzialmente enormi?
| Caratteristica / Considerazione | Incorporamento (Denormalizzazione) | Riferimento (Normalizzazione) |
|---|---|---|
| Prestazioni di Lettura | Eccellenti (Query Singola) | Buone a Discrete (Richiede join) |
| Prestazioni di Scrittura | Può essere costoso per documenti padre grandi o "caldi" | Spesso più semplice per documenti indipendenti |
| Limite di Dimensione dei Dati | Vincolato dal limite di 16MB del documento | Evita un enorme documento padre, ma richiede comunque indici e limiti di query progettati attentamente |
| Tipo di Relazione | Uno-a-Pochi | Uno-a-Molti, Molti-a-Molti |
| Coerenza dei Dati | Alta (Scritture atomiche) | Gestita manualmente (Potenziale obsolescenza) |
Suggerimento per le Buone Pratiche: Inizia con l'Incorporamento, Poi Cambia
Una strategia comune ed efficace è iniziare incorporando i dati che sai di leggere frequentemente insieme. Questo ottimizza per il caso comune. Se in seguito incontri colli di bottiglia nelle prestazioni a causa della grande crescita del documento o dell'eccessiva complessità degli aggiornamenti, puoi spostare quel pezzo specifico di dati nella sua collezione e passare al riferimento.
Conclusione
Gli schemi MongoDB funzionano meglio quando corrispondono alle tue query reali. Incorpora i dati che leggi insieme e che puoi mantenere limitati. Riferisci i dati che crescono indipendentemente, sono condivisi da molti padri o cambiano secondo la propria pianificazione. Prima di decidere un modello, scrivi le tue principali letture e scritture, poi testa quei percorsi con dimensioni di documento realistiche.