Je sais qu'il y a des questions similaires ici, mais elles me disent soit de revenir aux systèmes SGBDR réguliers si j'ai besoin de transactions, soit d'utiliser des opérations atomiques ou une validation en deux phases . La deuxième solution semble le meilleur choix. Le troisième je ne souhaite pas suivre car il semble que beaucoup de choses pourraient mal tourner et je ne peux pas le tester dans tous les aspects. J'ai du mal à refactoriser mon projet pour effectuer des opérations atomiques. Je ne sais pas si cela vient de mon point de vue limité (je n'ai travaillé qu'avec des bases de données SQL jusqu'à présent), ou si cela ne peut pas être fait.
Nous souhaitons tester MongoDB dans notre entreprise. Nous avons choisi un projet relativement simple - une passerelle SMS. Il permet à notre logiciel d'envoyer des SMS au réseau cellulaire et la passerelle fait le sale boulot: communiquer réellement avec les fournisseurs via différents protocoles de communication. La passerelle gère également la facturation des messages. Chaque client qui demande le service doit acheter des crédits. Le système diminue automatiquement le solde de l'utilisateur lorsqu'un message est envoyé et refuse l'accès si le solde est insuffisant. De plus, parce que nous sommes clients de fournisseurs de SMS tiers, nous pouvons également avoir nos propres soldes avec eux. Nous devons également en garder une trace.
J'ai commencé à réfléchir à la façon dont je pouvais stocker les données requises avec MongoDB si je réduisais une certaine complexité (facturation externe, envoi de SMS en file d'attente). Venant du monde SQL, je créerais une table séparée pour les utilisateurs, une autre pour les messages SMS et une pour stocker les transactions concernant le solde des utilisateurs. Disons que je crée des collections séparées pour toutes celles de MongoDB.
Imaginez une tâche d'envoi de SMS avec les étapes suivantes dans ce système simplifié:
vérifier si l'utilisateur a un solde suffisant; refuser l'accès s'il n'y a pas assez de crédit
envoyer et stocker le message dans la collection SMS avec les détails et le coût (dans le système en direct, le message aurait un
status
attribut et une tâche le ramasserait pour la livraison et fixerait le prix du SMS en fonction de son état actuel)diminuer le solde des utilisateurs par le coût du message envoyé
consigner la transaction dans la collection de transactions
Quel est le problème avec ça? MongoDB ne peut effectuer des mises à jour atomiques que sur un seul document. Dans le flux précédent, il peut arriver qu'une sorte d'erreur se glisse et que le message soit stocké dans la base de données, mais le solde de l'utilisateur n'est pas mis à jour et / ou la transaction n'est pas enregistrée.
J'ai eu deux idées:
Créez une collection unique pour les utilisateurs et stockez le solde sous forme de champ, les transactions et messages liés à l'utilisateur en tant que sous-documents dans le document de l'utilisateur. Parce que nous pouvons mettre à jour les documents de manière atomique, cela résout en fait le problème de transaction. Inconvénients: si l'utilisateur envoie de nombreux SMS, la taille du document peut devenir importante et la limite de 4 Mo de document peut être atteinte. Peut-être que je peux créer des documents d'histoire dans de tels scénarios, mais je ne pense pas que ce soit une bonne idée. De plus, je ne sais pas à quelle vitesse le système serait si je transmettais de plus en plus de données au même gros document.
Créez une collection pour les utilisateurs et une pour les transactions. Il peut y avoir deux types de transactions: achat à crédit avec changement de solde positif et messages envoyés avec changement de solde négatif. La transaction peut avoir un sous-document; par exemple, dans les messages envoyés, les détails du SMS peuvent être intégrés dans la transaction. Inconvénients: je ne stocke pas le solde actuel de l'utilisateur, je dois donc le calculer à chaque fois qu'un utilisateur essaie d'envoyer un message pour dire si le message peut passer ou non. Je crains que ce calcul ne devienne lent à mesure que le nombre de transactions stockées augmente.
Je ne sais pas trop quelle méthode choisir. Y a-t-il d'autres solutions? Je n'ai pas trouvé de bonnes pratiques en ligne sur la manière de contourner ce type de problèmes. Je suppose que de nombreux programmeurs qui essaient de se familiariser avec le monde NoSQL sont confrontés à des problèmes similaires au début.
la source
Réponses:
À partir de 4.0, MongoDB aura des transactions ACID multi-documents. Le plan consiste à activer les déploiements de jeux de réplicas en premier, suivis des clusters fragmentés. Les transactions dans MongoDB se sentiront exactement comme les transactions que les développeurs connaissent à partir de bases de données relationnelles - elles seront multi-instructions, avec une sémantique et une syntaxe similaires (comme
start_transaction
etcommit_transaction
). Il est important de noter que les modifications apportées à MongoDB qui activent les transactions n'ont pas d'impact sur les performances des charges de travail qui n'en ont pas besoin.Pour plus de détails, cliquez ici .
Avoir des transactions distribuées ne signifie pas que vous devez modéliser vos données comme dans des bases de données relationnelles tabulaires. Adoptez la puissance du modèle de document et suivez les bonnes pratiques recommandées de modélisation des données.
la source
Vivre sans transactions
Les transactions prennent en charge les propriétés ACID , mais bien qu'il n'y ait pas de transactions
MongoDB
, nous avons des opérations atomiques. Eh bien, les opérations atomiques signifient que lorsque vous travaillez sur un seul document, ce travail sera terminé avant que quiconque ne voie le document. Ils verront tous les changements que nous avons faits ou aucun d'entre eux. Et en utilisant des opérations atomiques, vous pouvez souvent accomplir la même chose que nous aurions accomplie en utilisant des transactions dans une base de données relationnelle. Et la raison en est que, dans une base de données relationnelle, nous devons apporter des modifications à plusieurs tables. Habituellement, des tables doivent être jointes et nous voulons donc tout faire en même temps. Et pour ce faire, comme il y a plusieurs tables, nous devrons commencer une transaction et faire toutes ces mises à jour, puis terminer la transaction. Mais avecMongoDB
, nous allons intégrer les données, car nous allons les pré-joindre dans des documents et ce sont ces documents riches qui ont une hiérarchie. Nous pouvons souvent accomplir la même chose. Par exemple, dans l'exemple de blog, si nous voulions nous assurer que nous avons mis à jour un article de blog de manière atomique, nous pouvons le faire car nous pouvons mettre à jour l'intégralité de l'article de blog à la fois. Là où, comme s'il s'agissait d'un tas de tables relationnelles, nous devrons probablement ouvrir une transaction afin de pouvoir mettre à jour la collection d'articles et la collection de commentaires.Alors, quelles sont nos approches que nous pouvons adopter
MongoDB
pour surmonter un manque de transactions?Update
,findAndModify
,$addToSet
(Dans une mise à jour) et$push
(dans une mise à jour) les opérations fonctionnent atomiquement dans un seul document.la source
Vérifiez ceci , par Tokutek. Ils développent un plugin pour Mongo qui promet non seulement des transactions mais également une augmentation des performances.
la source
Amenez-le à l' essentiel : si l'intégrité transactionnelle est indispensable, n'utilisez pas MongoDB mais utilisez uniquement les composants du système prenant en charge les transactions. Il est extrêmement difficile de construire quelque chose au-dessus du composant afin de fournir une fonctionnalité similaire à ACID pour les composants non conformes à ACID. Selon les cas d'utilisation individuels, il peut être judicieux de séparer les actions en actions transactionnelles et non transactionnelles d'une manière ou d'une autre ...
la source
Ce n'est pas vraiment un problème. L'erreur que vous avez mentionnée est soit une erreur logique (bogue), soit une erreur d'E / S (réseau, panne de disque). Un tel type d'erreur peut laisser les magasins sans transaction et transactionnels dans un état non cohérent. Par exemple, s'il a déjà envoyé des SMS mais lors du stockage, une erreur s'est produite - il ne peut pas annuler l'envoi de SMS, ce qui signifie qu'il ne sera pas enregistré, le solde de l'utilisateur ne sera pas réduit, etc.
Le vrai problème ici est que l'utilisateur peut profiter des conditions de course et envoyer plus de messages que son solde ne le permet. Cela s'applique également au SGBDR, sauf si vous envoyez des SMS à l'intérieur d'une transaction avec verrouillage du champ de solde (ce qui serait un grand goulot d'étranglement). Comme solution possible pour MongoDB, il faudrait d'abord utiliser
findAndModify
pour réduire le solde et le vérifier, s'il est négatif, interdire l'envoi et rembourser le montant (incrément atomique). S'il est positif, continuez à envoyer et en cas d'échec, rembourser le montant. La collection d'historique de solde peut également être gérée pour aider à corriger / vérifier le champ de solde.la source
Le projet est simple, mais vous devez prendre en charge les transactions pour le paiement, ce qui rend le tout difficile. Ainsi, par exemple, un système de portail complexe avec des centaines de collections (forum, chat, publicités, etc.) est à certains égards plus simple, car si vous perdez une entrée de forum ou de chat, personne ne s'en soucie vraiment. Si, par contre, vous perdez une transaction de paiement, c'est un problème grave.
Donc, si vous voulez vraiment un projet pilote pour MongoDB, choisissez-en un qui soit simple à cet égard.
la source
Les transactions sont absentes de MongoDB pour des raisons valables. C'est l'une de ces choses qui accélèrent MongoDB.
Dans votre cas, si la transaction est un must, mongo ne semble pas un bon choix.
Peut-être RDMBS + MongoDB, mais cela ajoutera des complexités et rendra plus difficile la gestion et le support de l'application.
la source
C'est probablement le meilleur blog que j'ai trouvé concernant l'implémentation d'une fonctionnalité de type transaction pour mongodb.!
Indicateur de synchronisation: idéal pour copier simplement des données à partir d'un document maître
Job Queue: à usage très général, résout 95% des cas. La plupart des systèmes doivent de toute façon avoir au moins une file d'attente de travaux!
Validation en deux phases: cette technique garantit que chaque entité dispose toujours de toutes les informations nécessaires pour atteindre un état cohérent
Log Reconciliation: la technique la plus robuste, idéale pour les systèmes financiers
Gestion des versions: assure l'isolation et prend en charge les structures complexes
Lisez ceci pour plus d'informations: https://dzone.com/articles/how-implement-robust-and
la source
C'est tard, mais je pense que cela aidera à l'avenir. J'utilise Redis pour créer une file d' attente afin de résoudre ce problème.
Condition: L'
image ci-dessous montre que 2 actions doivent être exécutées simultanément, mais les phases 2 et 3 de l'action 1 doivent être terminées avant de démarrer la phase 2 de l'action 2 ou inversement (une phase peut être une requête REST api, une requête de base de données ou exécuter du code javascript ... ).
Comment une file d'attente vous aide
Queue assurez-vous que chaque code de bloc entre
lock()
etrelease()
dans plusieurs fonctions ne s'exécutera pas en même temps, faites-les isoler.Comment créer une file d'attente
Je me concentrerai uniquement sur la façon d'éviter la partie de condition de course lors de la création d'une file d'attente sur le site backend. Si vous ne connaissez pas l'idée de base de la file d'attente, venez ici .
Le code ci-dessous ne montre que le concept, vous devez l'implémenter de manière correcte.
Mais vous devez vous
isRunning()
setStateToRelease()
setStateToRunning()
isoler ou bien vous affrontez à nouveau la condition de la race. Pour ce faire, j'ai choisi Redis à des fins ACID et évolutif.Le document Redis parle de sa transaction:
P / s:
J'utilise Redis parce que mon service l'utilise déjà, vous pouvez utiliser n'importe quel autre moyen d'isolation de support pour le faire.
Le
action_domain
dans mon code est ci-dessus lorsque vous n'avez besoin que de l'appel de l'action 1 par l'utilisateur A bloquer l'action 2 de l'utilisateur A, ne pas bloquer un autre utilisateur. L'idée est de mettre une clé unique pour la serrure de chaque utilisateur.la source
Les transactions sont désormais disponibles dans MongoDB 4.0. Échantillon ici
la source