Les systèmes de base de données ont pour mandat de garantir la cohérence et l'intégrité des données, en particulier lorsque des données critiques sont impliquées. Ces aspects sont appliqués via des transactions ACID dans MongoDB. Une transaction ACID doit respecter certaines règles définies pour la validité des données avant d'apporter des mises à jour à la base de données, sinon elle doit être abandonnée et aucune modification ne doit être apportée à la base de données. Toutes les transactions de base de données sont considérées comme une seule opération logique et pendant le temps d'exécution, la base de données est mise dans un état incohérent jusqu'à ce que les modifications aient été validées. Les opérations qui modifient avec succès l'état de la base de données sont appelées transactions d'écriture, tandis que celles qui ne mettent pas à jour la base de données mais ne récupèrent que des données sont appelées transactions en lecture seule. ACID est un acronyme pour Atomicité, Cohérence, Isolation et Durabilité.
Une base de données est une ressource partagée accessible par différents utilisateurs à des moments différents ou en même temps. Pour cette raison, des transactions simultanées peuvent se produire et si elles ne sont pas bien gérées, elles peuvent entraîner des plantages du système, une panne matérielle, un blocage, des performances lentes de la base de données ou une répétition dans l'exécution de la même transaction.
Que sont les règles ACID ?
Tous les systèmes de base de données doivent répondre aux propriétés ACID afin de garantir l'intégrité des données.
Atomicité
Une transaction est considérée comme une seule unité d'opération qui peut réussir complètement ou échouer complètement. Une transaction ne peut pas être exécutée partiellement. Si une condition de consultation d'une transaction échoue, la transaction entière échouera complètement et la base de données restera inchangée. Par exemple, si vous souhaitez transférer des fonds du compte X vers Y, il y a ici deux transactions, la première consiste à retirer des fonds de X et la seconde à enregistrer les fonds dans Y. Si la première transaction échoue, l'ensemble la transaction sera abandonnée
Cohérence
Lorsqu'une opération est émise, avant son exécution, la base de données est dans un état cohérent et doit le rester après chaque transaction. Même s'il y a une mise à jour, la transaction doit toujours amener la base de données à un état valide, en maintenant les invariants de la base de données. Par exemple, vous ne pouvez pas supprimer une clé primaire qui a été référencée comme clé étrangère dans une autre collection. Toutes les données doivent respecter les contraintes définies pour empêcher la corruption des données par une transaction illégale.
Isolement
Plusieurs transactions s'exécutant simultanément sont exécutées sans s'affecter mutuellement et leur résultat devrait être le même si elles devaient être exécutées séquentiellement. Lorsque deux transactions ou plus modifient les mêmes documents dans MongoDB, il peut y avoir un conflit. La base de données détectera un conflit juste avant qu'il ne soit commis. La première opération d'acquisition d'un verrou sur le document se poursuivra tandis que l'autre échouera et un message d'erreur de conflit s'affichera.
Durabilité
Cela dicte qu'une fois la transaction validée, les modifications doivent être maintenues à tout moment, même en cas de défaillance du système, par exemple en raison de pannes de courant ou d'une déconnexion Internet.
Transactions ACID MongoDB
MongoDB est une base de données NoSQL basée sur des documents avec un schéma flexible. Les transactions ne sont pas des opérations qui doivent être exécutées pour chaque opération d'écriture, car elles entraînent un coût de performances plus élevé que les écritures d'un seul document. Avec une structure basée sur des documents et un modèle de données dénormalisé, le besoin de transactions sera minimisé. Étant donné que MongoDB permet l'intégration de documents, vous n'avez pas nécessairement besoin d'utiliser une transaction pour répondre à une opération d'écriture.
MongoDB version 4.0 fournit une prise en charge des transactions multi-documents pour les déploiements de jeux de répliques uniquement et probablement la version 4.2 étendra la prise en charge des déploiements fragmentés (selon leurs notes de publication).
Exemple de transaction :
Assurez-vous d'abord d'avoir un jeu de répliques en place. En supposant que vous ayez une base de données appelée app et une collection d'utilisateurs dans Mongo Shell, exécutez les commandes suivantes :
$mongos et vous devriez voir quelque chose comme username:PRIMARY>
$use app
$db.users.insert([{_id:1, name: ‘Brian’}, {_id:2, name: ‘Sheila’}, {_id:3, name: ‘James’}])
Nous devons démarrer une session pour notre transaction :
$db.getMongo().startSession() and you should see something like
session { "id" : UUID("dcfa8de5-627d-3b1c-a890-63c9a355520c") }
En utilisant cette session, nous pouvons ajouter plus d'utilisateurs en utilisant une transaction avec les commandes suivantes
$session.startTransaction()
session.getDatabase(‘app’).users.insert({_id:4, name: ‘Hitler’})
Vous serez présenté avec WriteResult({"nInsterted":2})
La transaction n'a pas encore été validée et le $db.users.find({}) normal ne nous donnera que les utilisateurs précédemment enregistrés. Mais si nous exécutons le
$session.getDatabase(“app”).users.find()
le dernier enregistrement ajouté sera disponible dans les résultats renvoyés. Pour valider cette transaction, nous exécutons la commande ci-dessous
$session.commitTransaction()
La modification de la transaction est stockée en mémoire, c'est pourquoi même après un échec, les données seront disponibles lors de la récupération.
Transactions ACID multi-documents dans MongoDB
Ce sont des opérations multi-instructions qui doivent être exécutées séquentiellement sans s'affecter. Pour l'exemple ci-dessus, nous pouvons créer deux transactions, une pour ajouter un utilisateur et une autre pour mettre à jour un utilisateur avec un champ d'âge. C'est-à-dire
$session.startTransaction()
db.users.insert({_id:6, name “Ibrahim”})
db.users.updateOne({_id:3 , {$set:{age:50}}})
session.commit_transaction()
Les transactions peuvent être appliquées à des opérations sur plusieurs documents contenus dans une ou plusieurs collections/bases de données. Les modifications dues à la transaction de document n'ont pas d'impact sur les performances des charges de travail non liées ou n'en ont pas besoin. Tant que la transaction n'est pas validée, les écritures non validées ne sont ni répliquées sur les nœuds secondaires ni lisibles en dehors des transactions.
Meilleures pratiques pour les transactions MongoDB
Les transactions multi-documents ne sont prises en charge que dans le moteur de stockage WiredTiger. Comme mentionné précédemment, très peu d'applications nécessiteraient des transactions et si c'est le cas, nous devrions essayer de les rendre courtes. Sinon, pour une seule transaction ACID, si vous essayez d'effectuer un nombre excessif d'opérations, cela peut entraîner une forte pression sur le cache WiredTiger. Le cache est toujours dicté pour conserver l'état de toutes les écritures ultérieures depuis la création de l'instantané le plus ancien. Cela signifie que les nouvelles écritures s'accumuleront dans le cache pendant toute la durée de la transaction et ne seront vidées qu'après la validation ou l'abandon des transactions en cours d'exécution sur les anciens instantanés. Pour optimiser les performances de la base de données sur la transaction, les développeurs doivent prendre en compte :
- Modifiez toujours un petit nombre de documents dans une transaction. Sinon, vous devrez diviser la transaction en différentes parties et traiter les documents en différents lots. Au maximum, traitez 1 000 documents à la fois.
- Des exceptions temporaires telles que l'attente pour élire des problèmes de réseau primaires et transitoires peuvent entraîner l'avortement de la transaction. Les développeurs doivent établir une logique pour réessayer la transaction si les erreurs définies se présentent.
- Configurez la durée optimale pour l'exécution de la transaction à partir des 60 secondes par défaut fournies par MongoDB. En outre, utilisez l'indexation afin qu'elle puisse permettre un accès rapide aux données dans la transaction. Vous avez également la possibilité d'affiner la transaction en traitant les délais d'attente en la divisant en lots qui permettent son exécution dans les délais.
- Décomposez votre transaction en un petit ensemble d'opérations afin qu'elle respecte les contraintes de taille de 16 Mo. Sinon, si l'opération ainsi que la description de l'oplog dépassent cette limite, la transaction sera abandonnée.
- Toutes les données relatives à une entité doivent être stockées dans une structure de document unique et riche. Cela permet de réduire le nombre de documents à mettre en cache lorsque différents champs vont être modifiés.
Limites des transactions
- Vous ne pouvez pas créer ou supprimer une collection dans une transaction.
- Les transactions ne peuvent pas effectuer d'écritures dans une collection limitée
- Les transactions prennent beaucoup de temps à s'exécuter et, d'une manière ou d'une autre, elles peuvent ralentir les performances de la base de données.
- La taille des transactions est limitée à 16 Mo, ce qui oblige à diviser celles qui ont tendance à dépasser cette taille en transactions plus petites.
- Soumettre un grand nombre de documents à une transaction peut exercer une pression excessive sur le moteur WiredTiger et puisqu'il s'appuie sur la capacité d'instantané, il y aura une rétention de grandes opérations non vidées en mémoire. Cela entraîne un certain coût de performance sur la base de données.
Conclusion
MongoDB version 4.0 a introduit la prise en charge des transactions multi-documents pour les jeux de réplicas en tant que fonctionnalité d'amélioration de l'intégrité et de la cohérence des données. Cependant, il existe très peu d'applications nécessitant des transactions lors de l'utilisation de MongoDB. Il existe des limitations à cette fonctionnalité qui la rendent considérablement immature en ce qui concerne le concept de transactions. Par exemple, les transactions pour un cluster partitionné ne sont pas prises en charge et elles ne peuvent pas dépasser la limite de taille de 16 Mo. La modélisation des données fournit une meilleure structure pour réduire les transactions dans votre base de données. Sauf si vous avez affaire à des cas particuliers, il sera préférable d'éviter les transactions dans MongoDB.