Dans mon blog précédent, Comment utiliser la modélisation des données MongoDB pour améliorer les opérations de débit, nous avons discuté des 2 principales approches de relation de modélisation des données, à savoir l'intégration et le référencement. L'évolutivité de MongoDB dépend assez de son architecture et, plus précisément, de la modélisation des données. Lors de la conception d'un DBM NoSQL, le principal point à considérer est de garantir des documents sans schéma en plus d'un petit nombre de collections dans le but d'une maintenance facile. Une bonne intégrité des données, l'adoption de la validation des données par le biais de certaines règles définies avant le stockage est encouragée. Une architecture et une conception de base de données doivent être normalisées et décomposées en plusieurs petites collections afin d'éviter la répétition des données, d'améliorer l'intégrité des données et de faciliter la tâche avec des modèles de récupération. Avec cela en place, vous êtes en mesure d'améliorer la cohérence des données, l'atomicité, la durabilité et l'intégrité de votre base de données.
La modélisation des données n'est pas une entreprise après coup dans une phase de développement d'application, mais une considération initiale puisque de nombreuses facettes de l'application sont en fait réalisées au cours de l'étape de modélisation des données. Dans cet article, nous allons discuter des facteurs à prendre en compte lors de la modélisation des données et voir comment ils affectent les performances d'une base de données en général.
Souvent, vous devrez déployer un cluster de votre base de données pour augmenter la disponibilité des données. Avec un modèle de données bien conçu, vous pouvez répartir plus efficacement les activités sur un cluster partitionné, réduisant ainsi le débit des opérations destinées à une seule instance mongod. Les principaux facteurs à prendre en compte dans la modélisation des données incluent :
- Évolutivité
- Atomicité
- Performances et utilisation des données
- Partage
- Indexation
- Optimisation du stockage
- Structure et croissance du document
- Cycle de vie des données
1. Évolutivité
Il s'agit d'une augmentation de la charge de travail d'une application entraînée par un trafic accru. De nombreuses applications ont toujours une attente dans l'augmentation du nombre de ses utilisateurs. Lorsqu'il y a autant d'utilisateurs servis par une seule instance de base de données, les performances ne répondent pas toujours aux attentes. En tant que gestionnaire de base de données, vous avez donc pour mandat de concevoir ce DBM de sorte que les collections et les entités de données soient modélisées en fonction des demandes présentes et futures de l'application. La structure de la base de données doit généralement être présentable pour faciliter le processus de réplication et de partitionnement. Lorsque vous avez plus de partitions, les opérations d'écriture sont réparties entre ces partitions de sorte que pour toute mise à jour de données, cela se fait dans la partition contenant ces données plutôt que de rechercher dans un seul grand ensemble de données pour effectuer une mise à jour.
2. Atomicité
Il s'agit de la réussite ou de l'échec d'une opération en tant qu'unité unique. Par exemple, vous pouvez avoir une opération de lecture qui implique une opération de tri après avoir récupéré le résultat. Si l'opération de tri n'est pas gérée correctement, l'ensemble de l'opération ne passera donc pas à l'étape suivante.
Les transactions atomiques sont des séries d'opérations qui ne sont ni divisibles ni réductibles et se produisent donc en tant qu'entités uniques ou échouent en tant qu'opérations uniques. Les versions de MongoDB antérieures à 4.0 prennent en charge les opérations d'écriture en tant que processus atomiques sur un seul niveau de document. Avec la version 4.0, on peut désormais implémenter des transactions multi-documents. Un modèle de données qui améliore les opérations atomiques a tendance à avoir de bonnes performances en termes de latence. La latence est simplement la durée pendant laquelle une demande d'opération est envoyée et lorsqu'une réponse est renvoyée de la base de données. Pour être sécant, il est facile de mettre à jour des données qui sont intégrées dans un seul document plutôt que dans un document référencé.
Considérons par exemple l'ensemble de données ci-dessous
{
childId : "535523",
studentName : "James Karanja",
parentPhone : 704251068,
age : 12,
settings : {
location : "Embassy",
address : "420 01",
bus : "KAZ 450G",
distance : "4"
}
}
Si nous voulons mettre à jour l'âge en l'augmentant de 1 et changer l'emplacement à Londres, nous pourrions faire :
db.getCollection(‘students’).update({childId: 535523},{$set:{'settings.location':'London'}, $inc:{age:1}}).
Si par exemple l'opération $set échoue, alors automatiquement l'opération $inc ne sera pas implémentée et en général toute l'opération échoue.
D'autre part, considérons les données référencées telles qu'il existe 2 collections l'une pour l'élève et l'autre pour les paramètres.
Collection étudiante
{
childId : "535523",
studentName : "James Karanja",
parentPhone : 704251068,
age : 12
}
Collection de paramètres
{
childId : "535523",
location : "Embassy",
address : "420 01",
bus : "KAZ 450G",
distance : "4"
}
Dans ce cas, vous pouvez mettre à jour les valeurs d'âge et de lieu avec des opérations d'écriture distinctes, c'est-à-dire
db.getCollection(‘students’).update({childId: 535523},{$inc:{age:1}})
db.getCollection('settings’).update({childId: 535523 } , {$set: { 'settings.location':'London'}})
Si l'une des opérations échoue, cela n'affecte pas nécessairement l'autre puisqu'elles sont effectuées en tant qu'entités différentes.
Transactions pour plusieurs documents
Avec MongoDB version 4.0, vous pouvez désormais effectuer plusieurs transactions de documents pour des jeux de répliques. Cela améliore les performances puisque les opérations sont envoyées à un certain nombre de collections, de bases de données et de documents pour un traitement rapide. Lorsqu'une transaction a été validée, les données sont enregistrées, tandis que si quelque chose ne va pas et qu'une transaction échoue, les modifications apportées sont ignorées et la transaction est généralement abandonnée. Il n'y aura pas de mise à jour des jeux de répliques pendant la transaction puisque l'opération n'est visible à l'extérieur que lorsque la transaction est entièrement validée.
Autant que vous pouvez mettre à jour plusieurs documents dans plusieurs transactions, cela s'accompagne d'un revers de performances réduites par rapport aux écritures de documents uniques. En outre, cette approche n'est prise en charge que pour le moteur de stockage WiredTiger, ce qui constitue un inconvénient pour les moteurs de stockage In-Memory et MMAPv1.
3. Performances et utilisation des données
Les applications sont conçues différemment pour répondre à des objectifs différents. Il y en a qui ne servent que pour les données actuelles comme les applications d'actualités météo. Selon la structure d'une application, on devrait pouvoir concevoir une base de données optimale correspondante pour servir le cas d'utilisation requis. Par exemple, si l'on développe une application qui récupère les données les plus récentes de la base de données, l'utilisation d'une collection plafonnée sera la meilleure option. Une collection plafonnée améliore le fonctionnement à haut débit, tout comme un tampon, de sorte que lorsque l'espace alloué est exploité, les documents les plus anciens sont écrasés et les documents peuvent être récupérés dans l'ordre dans lequel ils ont été insérés. Compte tenu de la récupération de l'ordre d'insertion, il n'y aura pas besoin d'utiliser l'indexation et l'absence de surcharge d'index améliorera également le débit d'écriture. Avec une collection plafonnée, les données associées sont assez petites dans la mesure où elles peuvent être conservées dans la RAM pendant un certain temps. Dans ce cas, les données temporelles sont stockées dans le cache qui est plutôt lu qu'écrit, ce qui rend l'opération de lecture assez rapide. Cependant, la collection plafonnée présente certains inconvénients tels que, vous ne pouvez pas supprimer un document à moins de supprimer toute la collection, toute modification de la taille d'un document échouera l'opération et enfin, il n'est pas possible de fragmenter une collection plafonnée.
Différentes facettes sont intégrées dans la modélisation des données d'une base de données en fonction des besoins d'utilisation. Comme on l'a vu, les applications de rapport auront tendance à être plus intensives en lecture, par conséquent, la conception devrait être de manière à améliorer le débit de lecture.
4. Partage
Les performances grâce à la mise à l'échelle horizontale peuvent être améliorées par le partitionnement puisque les charges de travail de lecture et d'écriture sont réparties entre les membres du cluster. Le déploiement d'un cluster de partitions a tendance à partitionner la base de données en plusieurs petites collections avec des documents distribués en fonction d'une clé de partition. Vous devez sélectionner une clé de partition appropriée qui peut empêcher l'isolation des requêtes en plus d'augmenter la capacité d'écriture. Une meilleure sélection porte généralement sur un champ présent dans tous les documents de la collection ciblée. Avec le partitionnement, le stockage augmente car à mesure que les données augmentent, davantage de fragments sont créés pour contenir un sous-ensemble de ce cluster.
5. Indexation
L'indexation est l'une des meilleures approches pour améliorer la charge de travail d'écriture, en particulier lorsque les champs apparaissent dans tous les documents. Lors de l'indexation, il faut considérer que chaque index nécessitera 8 Ko d'espace de données. De plus, lorsque l'index est actif, il consomme de l'espace disque et de la mémoire et doit donc être suivi pour la planification de la capacité.
Plusieursnines Devenez un administrateur de base de données MongoDB – Amener MongoDB en productionDécouvrez ce que vous devez savoir pour déployer, surveiller, gérer et faire évoluer MongoDBDélécharger gratuitement6. Optimisation du stockage
De nombreux petits documents au sein d'une collection auront tendance à prendre plus d'espace que lorsque vous avez quelques documents avec des documents sous-incorporés. Lors de la modélisation, il convient donc de regrouper les données associées avant de les stocker. Avec quelques documents, une opération de base de données peut être effectuée avec peu de requêtes, d'où un accès disque aléatoire réduit et il y aura moins d'entrées de clé associées dans l'index correspondant. Les considérations dans ce cas seront donc :utiliser l'intégration pour avoir moins de documents, ce qui à son tour réduit la surcharge par document. Utilisez des noms de champ plus courts si moins de champs sont impliqués dans une collection afin de ne pas rendre la surcharge du document significative. Des noms de champ plus courts réduisent l'expressivité, c'est-à-dire
{ Lname : "Briston", score : 5.9 }
économisera 9 octets par document plutôt que d'utiliser
{ last_name : "Briston", high_score: 5.9 }
Utilisez explicitement le champ _id. Par défaut, les clients MongoDB ajoutent un champ _id à chaque document en attribuant un ObjectId unique de 12 octets pour ce champ. De plus, le champ _id sera indexé. Si les documents sont assez petits, ce scénario représentera une quantité importante d'espace dans le nombre total de documents. Pour l'optimisation du stockage, vous êtes autorisé à spécifier explicitement la valeur du champ _id lors de l'insertion de documents dans une collection. Cependant, assurez-vous que la valeur est identifiée de manière unique, car elle sert de clé primaire pour les documents de la collection.
7. Structure et croissance du document
Cela se produit à la suite de l'opération push où les sous-documents sont poussés dans un champ de tableau ou lorsque de nouveaux champs sont ajoutés à un document existant. La croissance du document a quelques revers, c'est-à-dire pour une collection plafonnée, si la taille est modifiée, l'opération échouera automatiquement. Pour un moteur de stockage MMAPv1, les versions antérieures à 3.0 déplaceront le document sur le disque si la taille du document est dépassée. Cependant, dans les versions ultérieures à partir de la version 3.0, il existe un concept d'allocations de puissance de 2 tailles qui réduit les chances de telles réallocations et permet la réutilisation efficace de l'espace d'enregistrement libéré. Si vous vous attendez à ce que vos données augmentent, vous souhaiterez peut-être refactoriser votre modèle de données pour utiliser des références entre les données dans des documents distincts plutôt que d'utiliser un modèle de données dénormalisé. Pour éviter la croissance des documents, vous pouvez également envisager d'utiliser une stratégie de pré-allocation.
8. Cycle de vie des données
Pour une application qui utilise uniquement les documents récemment insérés, envisagez d'utiliser une collection limitée dont les fonctionnalités ont été décrites ci-dessus.
Vous pouvez également définir la fonction Time to Live pour votre collection. Ceci est tout à fait applicable pour les jetons d'accès dans la fonction de réinitialisation de mot de passe pour une application.
Durée de vie (TTL)
Il s'agit d'un paramètre de collecte qui permet à mongod de supprimer automatiquement les données après une durée spécifiée. Par défaut, ce concept s'applique aux données d'événement générées par la machine, aux journaux et aux informations de session qui doivent persister pendant une période de temps limitée.
Exemple :
db.log_events.createIndex( { "createdAt": 1 }, { expireAfterSeconds: 3600 } )
Nous avons créé un index createdAt et spécifié une valeur expireAfterSeconds de 3600, soit 1 heure après l'heure de création. Maintenant, si nous insérons un document comme :
db.log_events.insert( {
"createdAt": new Date(),
"logEvent": 2,
"logMessage": "This message was recorded."
} )
Ce document sera supprimé 1 heure après l'insertion.
Vous pouvez également définir une heure spécifique à laquelle vous souhaitez que le document soit supprimé. Pour ce faire, créez d'abord un index, c'est-à-dire :
db.log_events.createIndex( { "expireAt": 1 }, { expireAfterSeconds: 0 } )
Nous pouvons maintenant insérer un document et spécifier l'heure à laquelle il doit être supprimé.
db.log_events.insert( {
"expireAt": new Date(December 12, 2018 18:00:00'),
"logEvent": 2,
"logMessage": "Success!"
} )
Ce document sera supprimé automatiquement lorsque la valeur expireAt est plus ancienne que le nombre de secondes spécifié dans expireAfterSeconds, c'est-à-dire 0 dans ce cas.
Conclusion
La modélisation des données est une entreprise vaste pour toute conception d'application afin d'améliorer les performances de sa base de données. Avant d'insérer des données dans votre base de données, tenez compte des besoins de l'application et des meilleurs modèles de modèle de données à implémenter. En outre, des facettes importantes des applications ne peuvent être réalisées qu'avec la mise en œuvre d'un modèle de données approprié.