Choisir le bon modèle de données MongoDB : Documents embarqués vs. Documents référencés
La flexibilité de MongoDB en tant que base de données de documents permet aux développeurs de modéliser les relations entre les données de plusieurs façons. Contrairement aux bases de données relationnelles traditionnelles qui imposent strictement des schémas normalisés, MongoDB offre deux stratégies primaires et puissantes pour structurer les données liées au sein de vos collections : l'embarquement et le référencement. Choisir la bonne approche est crucial, car cela impacte directement les performances de l'application, la cohérence des données, la complexité des requêtes et l'évolutivité.
Ce guide explore en profondeur les compromis entre l'embarquement de documents au sein d'un document parent et le référencement de documents liés à travers différentes collections. Comprendre quand et comment appliquer ces techniques vous permettra de concevoir des schémas MongoDB efficaces et performants, adaptés aux modèles d'accès spécifiques de votre application.
Comprendre les stratégies de modélisation de données MongoDB
MongoDB organise les données en documents (similaires aux objets JSON) stockés dans des collections. Les relations entre ces documents peuvent être modélisées à l'aide de deux modèles fondamentaux :
- Embarquement (Dénormalisation) : Stocker les données liées directement à l'intérieur du document parent.
- Référencement (Normalisation) : Stocker uniquement une référence (comme un
_id) au document lié dans une autre collection, similaire à une clé étrangère.
1. Le modèle d'embarquement (Dénormalisation)
L'embarquement consiste à placer un document directement à l'intérieur d'un autre. Cette technique est fortement privilégiée dans MongoDB lorsque les relations de données sont de type un-vers-peu ou lorsque les données liées sont fréquemment accédées avec le document parent.
Quand utiliser l'embarquement
Utilisez le modèle d'embarquement lorsque :
- Les données sont accédées ensemble : Si vous avez presque toujours besoin des données liées lors de l'interrogation du parent, l'embarquement minimise le nombre d'opérations de base de données nécessaires pour récupérer l'ensemble complet d'informations.
- Relations un-vers-peu : Idéal pour les relations où le tableau de documents embarqués reste relativement petit et prévisible (par exemple, les 10 dernières activités de connexion d'un utilisateur, ou les lignes d'articles d'une commande).
- La cohérence des données est critique : Les données embarquées sont intrinsèquement cohérentes car elles résident au sein d'un seul document, ce qui simplifie les garanties d'atomicité fournies par les transactions ACID sur un seul document de MongoDB.
Exemple d'embarquement
Considérez un Produit et ses Critiques. Si les critiques sont fréquemment récupérées avec le produit et que le nombre total de critiques est gérable :
// Document de la collection Product
{
"_id": ObjectId("..."),
"name": "SSD Haute Performance",
"price": 129.99,
"reviews": [
{
"user": "Alice",
"rating": 5,
"comment": "Le disque le plus rapide de tous les temps !"
},
{
"user": "Bob",
"rating": 4,
"comment": "Excellent rapport qualité-prix."
}
]
}
Inconvénients de l'embarquement
- Limites de taille de document : Les documents MongoDB ont une limite de taille maximale de 16 Mo. Si le tableau de documents embarqués croît de manière illimitée, vous atteindrez finalement cette limite, nécessitant un passage au référencement.
- Surcharge de mise à jour : La mise à jour d'un seul élément embarqué nécessite de réécrire le document parent entier, ce qui peut être inefficace si le document parent est très volumineux.
- Duplication de données : Si les données embarquées doivent être partagées ou affichées indépendamment du parent, vous risquez une duplication de données et d'éventuels problèmes de cohérence si les mises à jour ne sont pas synchronisées sur toutes les copies.
2. Le modèle de référencement (Normalisation)
Le référencement imite le concept de clés étrangères dans les bases de données relationnelles. Au lieu d'embarquer les données liées, vous stockez l'_id (ou une combinaison d'IDs) du ou des documents liés dans le document parent. Cela nécessite une deuxième requête (une étape d'agrégation $lookup ou une jointure côté application) pour récupérer les données liées réelles.
Quand utiliser le référencement
Utilisez le modèle de référencement lorsque :
- Relations un-à-plusieurs ou plusieurs-à-plusieurs : Lorsqu'un côté de la relation peut croître indéfiniment (par exemple, le nombre de commentaires sur un article de blog, ou des utilisateurs appartenant à de nombreux groupes).
- Données partagées entre plusieurs parents : Si l'entité de données liée doit être mise à jour et accédée indépendamment par plusieurs autres documents (par exemple, un document
Categoryutilisé par de nombreux documentsProduct). - Grands ensembles de données : Lorsque l'embarquement violerait la limite de taille de document de 16 Mo.
Types de référencement
A. Références manuelles (Jointures côté application)
Stockage de l'_id dans le document parent :
// Collection Author
{
"_id": ObjectId("author123"),
"name": "Jane Doe"
}
// Collection Book
{
"_id": ObjectId("book456"),
"title": "Modélisation de données 101",
"author_id": ObjectId("author123") // Référence
}
Pour récupérer le nom de l'auteur, vous effectuez deux requêtes ou utilisez $lookup :
// Exemple utilisant $lookup dans le framework d'agrégation
db.books.aggregate([
{ $match: { title: "Modélisation de données 101" } },
{
$lookup: {
from: "authors", // Collection à joindre
localField: "author_id", // Champ des documents d'entrée (books)
foreignField: "_id", // Champ des documents de la collection 'from' (authors)
as: "author_details"
}
}
]);
B. Références bidirectionnelles
Pour les relations bidirectionnelles, vous pouvez également référencer le parent dans le document enfant. Cela facilite la traversée de la relation dans les deux sens, bien que cela augmente la surcharge d'écriture car les mises à jour doivent se produire à deux endroits.
Inconvénients du référencement
- Complexité accrue des requêtes : La récupération de données entièrement dénormalisées nécessite des jointures (soit via le code de l'application, soit via
$lookupde MongoDB), ce qui peut être plus lent qu'une seule opération de lecture embarquée. - Gestion de la cohérence : Si vous modifiez les données référencées (par exemple, renommer un auteur), vous devez mettre à jour manuellement tous les documents qui référencent cet auteur, ou accepter que certains documents affichent des données périmées jusqu'à leur rafraîchissement.
Résumé : Faire le bon choix
La décision entre l'embarquement et le référencement tourne autour des modèles d'accès. Demandez-vous : À quelle fréquence ces données liées sont-elles récupérées ? À quelle fréquence changent-elles ? Sont-elles petites ou potentiellement massives ?
| Fonctionnalité / Considération | Embarquement (Dénormalisation) | Référencement (Normalisation) |
|---|---|---|
| Performances en lecture | Excellentes (Requête unique) | Bonnes à correctes (Nécessite des jointures) |
| Performances en écriture | Mauvaises (Réécriture de document entier) | Bonnes (Mise à jour uniquement du point de référence) |
| Limite de taille des données | Liée à 16 Mo | Pas de limite pratique |
| Type de relation | Un-vers-peu | Un-à-plusieurs, Plusieurs-à-plusieurs |
| Cohérence des données | Élevée (Écritures atomiques) | Gérée manuellement (Potentielle obsolescence) |
Conseil de bonne pratique : Commencer par l'embarquement, pivoter plus tard
Une stratégie courante et efficace consiste à commencer par embarquer les données que vous savez être fréquemment lues ensemble. Cela optimise le cas commun. Si vous rencontrez plus tard des goulots d'étranglement de performance dus à une croissance importante des documents ou à une complexité de mise à jour excessive, vous pouvez pivoter cette pièce spécifique de données dans sa propre collection et passer au référencement.
Conclusion
MongoDB offre la flexibilité d'optimiser pour les lectures ou les écritures en fonction des besoins de votre application. L'embarquement sacrifie la simplicité de mise à jour pour un accès rapide en lecture lorsque les données sont étroitement couplées. Le référencement préserve l'intégrité des données et gère une croissance illimitée au prix d'opérations de lecture plus complexes impliquant des jointures. En analysant attentivement le rapport lecture/écriture de votre application et la cardinalité des relations, vous pouvez concevoir un schéma MongoDB qui maximise les performances et la maintenabilité.