Wie kann man den Mangel an Transaktionen in MongoDB umgehen?


139

Ich weiß, dass es hier ähnliche Fragen gibt, aber sie fordern mich entweder auf, zu regulären RDBMS-Systemen zurückzukehren, wenn ich Transaktionen benötige oder atomare Operationen oder ein zweiphasiges Festschreiben verwende . Die zweite Lösung scheint die beste Wahl zu sein. Das dritte möchte ich nicht verfolgen, da anscheinend viele Dinge schief gehen könnten und ich es nicht in jeder Hinsicht testen kann. Es fällt mir schwer, mein Projekt für atomare Operationen umzugestalten. Ich weiß nicht, ob dies aus meiner eingeschränkten Sicht geschieht (ich habe bisher nur mit SQL-Datenbanken gearbeitet) oder ob dies tatsächlich nicht möglich ist.

Wir möchten MongoDB in unserem Unternehmen pilotieren. Wir haben ein relativ einfaches Projekt ausgewählt - ein SMS-Gateway. Es ermöglicht unserer Software, SMS-Nachrichten an das Mobilfunknetz zu senden, und das Gateway erledigt die Drecksarbeit: Die Kommunikation mit den Anbietern erfolgt über verschiedene Kommunikationsprotokolle. Das Gateway verwaltet auch die Abrechnung der Nachrichten. Jeder Kunde, der sich für den Service bewirbt, muss ein Guthaben kaufen. Das System verringert automatisch das Guthaben des Benutzers, wenn eine Nachricht gesendet wird, und verweigert den Zugriff, wenn das Guthaben nicht ausreicht. Auch weil wir Kunden von SMS-Anbietern von Drittanbietern sind, haben wir möglicherweise auch unsere eigenen Guthaben bei ihnen. Wir müssen auch diese im Auge behalten.

Ich begann darüber nachzudenken, wie ich die erforderlichen Daten mit MongoDB speichern kann, wenn ich die Komplexität reduziere (externe Abrechnung, SMS-Versand in der Warteschlange). Aus der SQL-Welt stammend, würde ich eine separate Tabelle für Benutzer erstellen, eine andere für SMS-Nachrichten und eine zum Speichern der Transaktionen in Bezug auf den Kontostand der Benutzer. Angenommen, ich erstelle separate Sammlungen für alle in MongoDB.

Stellen Sie sich eine SMS-Sendeaufgabe mit den folgenden Schritten in diesem vereinfachten System vor:

  1. Überprüfen Sie, ob der Benutzer über ein ausreichendes Gleichgewicht verfügt. Zugriff verweigern, wenn nicht genügend Guthaben vorhanden ist

  2. Senden und Speichern der Nachricht in der SMS-Sammlung mit den Details und Kosten (im Live-System hätte die Nachricht ein statusAttribut und eine Aufgabe würde sie zur Zustellung abholen und den Preis der SMS entsprechend ihrem aktuellen Status festlegen).

  3. Verringern Sie das Guthaben der Benutzer um die Kosten der gesendeten Nachricht

  4. Protokollieren Sie die Transaktion in der Transaktionssammlung

Was ist das Problem damit? MongoDB kann atomare Aktualisierungen nur für ein Dokument durchführen. Im vorherigen Ablauf kann es vorkommen, dass sich ein Fehler einschleicht und die Nachricht in der Datenbank gespeichert wird, der Kontostand des Benutzers jedoch nicht aktualisiert und / oder die Transaktion nicht protokolliert wird.

Ich hatte zwei Ideen:

  • Erstellen Sie eine einzelne Sammlung für die Benutzer und speichern Sie den Kontostand als Feld, benutzerbezogene Transaktionen und Nachrichten als Unterdokumente im Benutzerdokument. Da wir Dokumente atomar aktualisieren können, wird das Transaktionsproblem dadurch gelöst. Nachteile: Wenn der Benutzer viele SMS-Nachrichten sendet, kann das Dokument groß werden und das 4-MB-Dokumentlimit erreicht werden. Vielleicht kann ich in solchen Szenarien Verlaufsdokumente erstellen, aber ich denke nicht, dass dies eine gute Idee wäre. Ich weiß auch nicht, wie schnell das System wäre, wenn ich immer mehr Daten auf dasselbe große Dokument übertragen würde.

  • Erstellen Sie eine Sammlung für Benutzer und eine für Transaktionen. Es gibt zwei Arten von Transaktionen: Kreditkauf mit positiver Kontostandänderung und Nachrichten mit negativer Kontostandänderung. Die Transaktion kann ein Unterdokument haben. Beispielsweise können in gesendeten Nachrichten die Details der SMS in die Transaktion eingebettet werden. Nachteile: Ich speichere das aktuelle Benutzerguthaben nicht, daher muss ich es jedes Mal berechnen, wenn ein Benutzer versucht, eine Nachricht zu senden, um festzustellen, ob die Nachricht durchlaufen werden kann oder nicht. Ich befürchte, diese Berechnung kann langsam werden, wenn die Anzahl der gespeicherten Transaktionen zunimmt.

Ich bin ein bisschen verwirrt darüber, welche Methode ich wählen soll. Gibt es andere Lösungen? Ich konnte online keine Best Practices finden, um diese Art von Problemen zu umgehen. Ich denke, viele Programmierer, die versuchen, sich mit der NoSQL-Welt vertraut zu machen, stehen am Anfang vor ähnlichen Problemen.


61
Verzeihen Sie mir, wenn ich falsch liege, aber es sieht so aus, als würde dieses Projekt einen NoSQL-Datenspeicher verwenden, unabhängig davon, ob es davon profitiert oder nicht. NoSQLs sind keine Alternative zu SQL als "Mode" -Wahl, aber wenn die Technologie relationaler RDBMS nicht in den Problembereich passt und ein nicht relationaler Datenspeicher. Viele Ihrer Fragen lauten "Wenn es SQL wäre, dann ..." und das läutet Warnglocken für mich. Alle NoSQLs sind aus der Notwendigkeit entstanden, ein Problem zu lösen, das SQL nicht lösen konnte, und dann wurden sie etwas verallgemeinert, um die Verwendung zu vereinfachen, und dann beginnt natürlich der Zug zu rollen.
PurplePilot

4
Mir ist bewusst, dass dieses Projekt nicht gerade das beste ist, um NoSQL auszuprobieren. Ich habe jedoch Angst, wenn wir anfangen, es mit anderen Projekten zu verwenden (sagen wir, eine Bibliothekssammlungsverwaltungssoftware, weil wir uns mit Sammlungsverwaltung beschäftigen), und plötzlich kommt eine Art Anfrage, die Transaktionen benötigt (und es ist tatsächlich da, stellen Sie sich vor, dass ein Buch wird von einer Sammlung in eine andere übertragen) Wir müssen wissen, wie wir das Problem überwinden können. Vielleicht bin nur ich engstirnig und denke, dass immer Transaktionen erforderlich sind. Aber es könnte einen Weg geben, diese irgendwie zu überwinden.
NagyI

3
Ich stimme PurplePilot zu. Sie sollten eine Technologie wählen, die zu einer Lösung passt, und nicht versuchen, eine Lösung zu entwickeln, die für ein Problem nicht geeignet ist. Das Modellieren von Daten für die Grafikdatenbanken ist ein völlig anderes Paradigma als das RDBMS-Design. Sie müssen alles vergessen, was Sie wissen, und die neue Denkweise neu lernen.

9
Ich verstehe, dass ich das geeignete Werkzeug für die Aufgabe verwenden sollte. Wenn ich jedoch solche Antworten lese, scheint es mir, dass NoSQL für nichts gut ist, wo Daten kritisch sind. Es ist gut für Facebook oder Twitter, wo, wenn einige Kommentare verloren gehen, die Welt weitergeht, aber alles darüber hinaus aus dem Geschäft ist. Wenn das stimmt, verstehe ich nicht, warum es anderen wichtig ist, z. Ein Webstore mit MongoDB: kylebanker.com/blog/2010/04/30/mongodb-and-ecommerce Es wird sogar erwähnt, dass die meisten Transaktionen mit atomaren Operationen überwunden werden können. Was ich suche, ist das Wie.
NagyI

2
Sie sagen, "es scheint, dass NoSQL für nichts gut ist, wo Daten kritisch sind", ist nicht wahr, wenn es nicht gut ist (vielleicht), ist eine transaktionale ACID-Transaktionsverarbeitung. NoSQLs sind auch für verteilte Datenspeicher konzipiert, deren Speicherung vom Typ SQL sehr schwierig sein kann, wenn Sie in die Master-Slave-Replikationsszenarien einsteigen. NoSQL verfügt über Strategien für eine eventuelle Konsistenz und stellt sicher, dass nur der neueste Datensatz verwendet wird, nicht jedoch ACID.
PurplePilot

Antworten:


23

Ab 4.0 verfügt MongoDB über ACID-Transaktionen mit mehreren Dokumenten. Es ist geplant, zuerst diejenigen in Replikatsatzbereitstellungen zu aktivieren, gefolgt von den Sharded-Clustern. Transaktionen in MongoDB fühlen sich wie Transaktionsentwickler an, die mit relationalen Datenbanken vertraut sind - sie bestehen aus mehreren Anweisungen mit ähnlicher Semantik und Syntax (wie start_transactionund commit_transaction). Wichtig ist, dass die Änderungen an MongoDB, die Transaktionen ermöglichen, keine Auswirkungen auf die Leistung von Workloads haben, für die sie nicht erforderlich sind.

Weitere Details finden Sie hier .

Wenn Sie verteilte Transaktionen haben, bedeutet dies nicht, dass Sie Ihre Daten wie in tabellarischen relationalen Datenbanken modellieren sollten. Nutzen Sie die Leistungsfähigkeit des Dokumentmodells und befolgen Sie die bewährten Methoden der Datenmodellierung.


1
Transaktionen sind angekommen! 4,0 GA'ed. mongodb.com/blog/post/…
Grigori Melnik

MongoDB-Transaktionen haben immer noch eine Beschränkung der Größe der Transaktion 16 MB. Vor kurzem hatte ich einen Anwendungsfall, in dem ich 50.000 Datensätze aus einer Datei in mongoDB ablegen musste. Um die atomare Eigenschaft beizubehalten, dachte ich daran, Transaktionen zu verwenden, aber seit 50.000 json-Datensätzen Wenn diese Grenze überschritten wird, wird der Fehler "Die Gesamtgröße aller Transaktionsvorgänge muss kleiner als 16793600 sein. Die tatsächliche Größe beträgt 16793817" ausgegeben. Weitere Informationen erhalten Sie über das offizielle Jira-Ticket, das unter mongoDB geöffnet ist. Jira.mongodb.org/browse/SERVER-36330
Gautam Malik

MongoDB 4.2 (derzeit in Beta, RC4) unterstützt große Transaktionen. Durch die Darstellung von Transaktionen über mehrere Oplog-Einträge hinweg können Sie mehr als 16 MB Daten in eine einzelne ACID-Transaktion schreiben (vorbehaltlich der vorhandenen maximalen Standardausführungszeit von 60 Sekunden). Sie können sie jetzt versuchen - mongodb.com/download-center/community
Grigori Melnik

MongoDB 4.2 ist jetzt GA mit voller Unterstützung für verteilte Transaktionen. mongodb.com/blog/post/…
Grigori Melnik

83

Leben ohne Transaktionen

Transaktionen unterstützen ACID- Eigenschaften, aber obwohl keine Transaktionen vorhanden sind MongoDB, haben wir atomare Operationen. Nun, atomare Operationen bedeuten, dass wenn Sie an einem einzelnen Dokument arbeiten, diese Arbeit abgeschlossen wird, bevor jemand anderes das Dokument sieht. Sie werden alle Änderungen sehen, die wir vorgenommen haben, oder keine von ihnen. Und mit atomaren Operationen können Sie oft das Gleiche erreichen, was wir mit Transaktionen in einer relationalen Datenbank erreicht hätten. Der Grund dafür ist, dass wir in einer relationalen Datenbank Änderungen über mehrere Tabellen hinweg vornehmen müssen. Normalerweise müssen Tabellen verbunden werden, und deshalb möchten wir das alles auf einmal tun. Und da es mehrere Tabellen gibt, müssen wir eine Transaktion beginnen und all diese Aktualisierungen durchführen und dann die Transaktion beenden. Aber mitMongoDBWir werden die Daten einbetten, da wir sie vorab in Dokumenten zusammenfügen und es sich um diese umfangreichen Dokumente handelt, die eine Hierarchie aufweisen. Wir können oft das Gleiche erreichen. Wenn wir beispielsweise im Blog-Beispiel sicherstellen möchten, dass wir einen Blog-Beitrag atomar aktualisiert haben, können wir dies tun, da wir den gesamten Blog-Beitrag auf einmal aktualisieren können. Wo es sich um eine Reihe relationaler Tabellen handelt, müssten wir wahrscheinlich eine Transaktion öffnen, damit wir die Sammlung von Posts und Kommentaren aktualisieren können.

Welche Ansätze können wir verfolgen MongoDB, um einen Mangel an Transaktionen zu überwinden?

  • restrukturieren - Restrukturieren Sie den Code so, dass wir in einem einzigen Dokument arbeiten und die atomaren Operationen nutzen, die wir in diesem Dokument anbieten. Und wenn wir das tun, sind wir normalerweise fertig.
  • In Software implementieren - Wir können das Sperren in Software implementieren, indem wir einen kritischen Abschnitt erstellen. Wir können einen Test erstellen, testen und festlegen, indem wir suchen und ändern. Bei Bedarf können wir Semaphoren erstellen. Und in gewisser Weise funktioniert die größere Welt sowieso so. Wenn wir darüber nachdenken, wenn eine Bank Geld an eine andere Bank überweisen muss, leben sie nicht im selben Beziehungssystem. Und jeder hat oft seine eigenen relationalen Datenbanken. Und sie müssen in der Lage sein, diesen Vorgang zu koordinieren, obwohl wir die Transaktion nicht über diese Datenbanksysteme hinweg starten und beenden können, sondern nur innerhalb eines Systems innerhalb einer Bank. Es gibt also sicherlich Möglichkeiten in der Software, um das Problem zu umgehen.
  • tolerieren - Der letzte Ansatz, der in modernen Webanwendungen und anderen Anwendungen, die eine enorme Datenmenge aufnehmen, häufig funktioniert, besteht darin, nur ein wenig Inkonsistenz zu tolerieren. Ein Beispiel wäre, wenn es sich um einen Freund-Feed in Facebook handelt, spielt es keine Rolle, ob jeder Ihr Wall-Update gleichzeitig sieht. Wenn ok, wenn eine Person ein paar Sekunden hinter sich hat und sie aufholt. Bei vielen Systemdesigns ist es oft nicht kritisch, dass alles perfekt konsistent bleibt und dass jeder eine perfekt konsistente und die gleiche Ansicht der Datenbank hat. Wir könnten also einfach ein bisschen Inkonsistenz tolerieren, die etwas vorübergehend ist.

Update, findAndModify, $addToSet(Innerhalb einer Aktualisierung) & $push(innerhalb einer Aktualisierung) Operationen arbeiten atomar in einem einzigen Dokument.


2
Ich mag die Art und Weise, wie diese Antwort funktioniert, anstatt immer wieder zu fragen, ob wir zur relationalen Datenbank zurückkehren sollten. Danke @xameeramir!
DonnyTian

3
Ein kritischer Codeabschnitt funktioniert nicht, wenn Sie mehr als einen Server haben und einen externen verteilten Sperrdienst verwenden müssen
Alexander Mills

@AlexanderMills Können Sie bitte näher darauf eingehen?
Zameer

Antwort scheint Video-Transkript von hier zu sein: youtube.com/watch?v=_Iz5xLZr8Lw
Fritz

Ich denke, das scheint in Ordnung zu sein, bis wir nur noch eine einzige Sammlung bearbeiten müssen. Wir können jedoch aus verschiedenen Gründen (Dokumentgröße oder wenn Sie Referenzen verwenden) nicht alles in ein einzelnes Dokument einfügen. Ich denke dann brauchen wir vielleicht Transaktionen.
user2488286

24

Überprüfen Sie diese heraus, durch Tokutek. Sie entwickeln ein Plugin für Mongo, das nicht nur Transaktionen verspricht, sondern auch die Leistung steigert.


@ Giovanni Bitliner. Tokutek wurde inzwischen von Percona übernommen, und auf dem von Ihnen angegebenen Link sehe ich keinen Hinweis auf Informationen zu irgendetwas, das seit dem Post passiert ist. Wissen Sie, was mit ihren Bemühungen passiert ist? Ich habe die E-Mail-Adresse auf dieser Seite per E-Mail gesendet, um dies herauszufinden.
Tyler Collier

Was brauchst du konkret? Wenn Sie eine Toku-Technologie benötigen, die auf Mongodb angewendet wird, versuchen Sie es mit github.com/Tokutek/mongo . Wenn Sie die MySQL-Version benötigen, haben sie diese möglicherweise zu ihrer Standardversion von MySQL hinzugefügt, die sie normalerweise bereitstellen
Giovanni Bitliner

Wie kann ich Tokutek mit NodeJS integrieren?
Manoj Sanjeewa

11

Bringen Sie es auf den Punkt: Wenn die Transaktionsintegrität ein Muss ist, verwenden Sie MongoDB nicht, sondern nur Komponenten im System, die Transaktionen unterstützen. Es ist äußerst schwierig, etwas auf Komponenten aufzubauen, um ACID-ähnliche Funktionen für nicht ACID-kompatible Komponenten bereitzustellen. Abhängig von den einzelnen Anwendungsfällen kann es sinnvoll sein, Aktionen in irgendeiner Weise in transaktionale und nicht-transaktionale Aktionen zu unterteilen ...


1
Ich denke, Sie meinen, NoSQL kann als Sidekick-Datenbank mit klassischem RDBMS verwendet werden. Ich mag die Idee nicht, NoSQL und SQL im selben Projekt zu mischen. Dies erhöht die Komplexität und führt möglicherweise auch zu einigen nicht trivialen Problemen.
NagyI

1
NoSQL-Lösungen werden selten alleine verwendet. Dokumentenspeicher (Mongo und Couch) sind wahrscheinlich die einzige Ausnahme von dieser Regel.
Karoly Horvath

7

Was ist das Problem damit? MongoDB kann atomare Aktualisierungen nur für ein Dokument durchführen. Im vorherigen Ablauf kann es vorkommen, dass sich ein Fehler einschleicht und die Nachricht in der Datenbank gespeichert wird, der Kontostand des Benutzers jedoch nicht verringert und / oder die Transaktion nicht protokolliert wird.

Das ist eigentlich kein Problem. Der von Ihnen erwähnte Fehler ist entweder ein logischer (Fehler) oder ein E / A-Fehler (Netzwerk, Festplattenfehler). Diese Art von Fehler kann dazu führen, dass sowohl transaktionslose als auch Transaktionsspeicher in einem nicht konsistenten Zustand bleiben. Wenn zum Beispiel bereits SMS gesendet wurden, aber beim Speichern der Nachricht ein Fehler aufgetreten ist, kann das Senden von SMS nicht rückgängig gemacht werden. Dies bedeutet, dass es nicht protokolliert wird, das Benutzerguthaben nicht verringert wird usw.

Das eigentliche Problem hierbei ist, dass der Benutzer die Rennbedingungen nutzen und mehr Nachrichten senden kann, als sein Kontostand zulässt. Dies gilt auch für RDBMS, es sei denn, Sie senden SMS innerhalb einer Transaktion mit Sperrfeldsperre (was ein großer Engpass wäre). Als mögliche Lösung für MongoDB würde findAndModifyzuerst verwendet, um das Guthaben zu reduzieren und es zu überprüfen, wenn es negativ ist, das Senden zu verbieten und den Betrag zurückzuerstatten (atomares Inkrement). Wenn dies positiv ist, senden Sie weiter und falls dies fehlschlägt, erstatten Sie den Betrag. Die Erfassung des Kontostandverlaufs kann auch verwaltet werden, um das Kontofeld zu korrigieren / zu überprüfen.


Vielen Dank für diese tolle Antwort! Ich weiß, dass wenn ich transaktionsfähige Speicher verwende, Daten aufgrund des SMS-Systems beschädigt werden können, über das ich keine Kontrolle habe. Bei Mongo besteht jedoch die Möglichkeit, dass Datenfehler auch intern auftreten. Angenommen, der Code ändert das Guthaben des Benutzers mit findAndModify. Das Guthaben wird negativ, aber bevor ich den Fehler korrigieren kann, tritt ein Fehler auf und die Anwendung muss neu gestartet werden. Ich denke, Sie meinen, ich sollte etwas Ähnliches wie ein Zwei-Phasen-Commit basierend auf der Transaktionssammlung implementieren und regelmäßige Korrekturprüfungen in der Datenbank durchführen.
NagyI

9
Nicht wahr, Transaktionsspeicher werden zurückgesetzt, wenn Sie kein endgültiges Commit durchführen.
Karoly Horvath

9
Außerdem senden Sie keine SMS und melden sich dann bei DB an. Das ist einfach falsch. Speichern Sie zuerst alles in der Datenbank und führen Sie ein endgültiges Commit durch. Dann können Sie die Nachricht senden. Zu diesem Zeitpunkt kann immer noch etwas fehlschlagen. Sie benötigen daher einen Cron-Job, um zu überprüfen, ob die Nachricht tatsächlich gesendet wurde, wenn Sie nicht versuchen, sie zu senden. Vielleicht wäre eine dedizierte Nachrichtenwarteschlange dafür besser. Aber das Ganze läuft darauf hinaus, ob Sie SMS auf transaktionale Weise senden können ...
Karoly Horvath

@ NagyIch ja, das habe ich gemeint. Man muss die Vorteile von Transaktionen für eine einfache Skalierbarkeit eintauschen. Grundsätzlich muss die Anwendung damit rechnen, dass zwei beliebige Dokumente in verschiedenen Sammlungen in einem inkonsistenten Zustand sein können und bereit sind, damit umzugehen. @yi_H wird zurückgesetzt, aber der Status wird nicht mehr aktuell sein (Informationen über die Nachricht gehen verloren). Dies ist nicht viel besser, als nur Teildaten zu haben (z. B. Kontostand reduziert, aber keine Nachrichteninformationen oder umgekehrt).
Pingw33n

Aha. Dies ist eigentlich keine einfache Einschränkung. Vielleicht sollte ich mehr darüber erfahren, wie RDBMS-Systeme Transaktionen ausführen. Können Sie eine Art Online-Material oder Buch empfehlen, in dem ich darüber lesen kann?
NagyI

6

Das Projekt ist einfach, aber Sie müssen Transaktionen für die Zahlung unterstützen, was das Ganze schwierig macht. So ist beispielsweise ein komplexes Portalsystem mit Hunderten von Sammlungen (Forum, Chat, Anzeigen usw.) in gewisser Hinsicht einfacher, denn wenn Sie ein Forum oder einen Chat-Eintrag verlieren, kümmert sich niemand wirklich darum. Wenn Sie andererseits einen Zahlungsvorgang verlieren, ist dies ein ernstes Problem.

Wenn Sie also wirklich ein Pilotprojekt für MongoDB möchten, wählen Sie eines, das in dieser Hinsicht einfach ist .


Danke für die Erklärung. Traurig das zu hören. Ich mag die Einfachheit von NoSQL und die Verwendung von JSON. Wir suchen nach einer Alternative zu ORM, aber es sieht so aus, als müssten wir eine Weile dabei bleiben.
NagyI

Können Sie gute Gründe nennen, warum MongoDB für diese Aufgabe besser als SQL ist? Pilotprojekt klingt ein bisschen albern.
Karoly Horvath

Ich habe nicht gesagt, dass MongoDB besser ist als SQL. Wir wollen einfach nur wissen, ob es besser ist als SQL + ORM. Aber jetzt wird klarer, dass sie bei solchen Projekten nicht wettbewerbsfähig sind.
NagyI

6

Transaktionen fehlen in MongoDB aus triftigen Gründen. Dies ist eines der Dinge, die MongoDB schneller machen.

In Ihrem Fall, wenn Transaktion ein Muss ist, scheint Mongo nicht gut zu passen.

Möglicherweise RDMBS + MongoDB, dies erhöht jedoch die Komplexität und erschwert die Verwaltung und Unterstützung von Anwendungen.


1
Es gibt jetzt eine MongoDB-Distribution namens TokuMX, die fraktale Technologie verwendet, um eine 50-fache Leistungsverbesserung zu erzielen und gleichzeitig vollständige ACID-Transaktionsunterstützung bietet: tokutek.com/tokumx-for-mongodb
OCDev

9
Wie könnte eine Transaktion jemals kein "Muss" sein? Sobald Sie jemals einen einfachen Fall benötigen, in dem Sie 2 Tabellen aktualisieren müssen, passt Mongo plötzlich nicht mehr? Das lässt nicht sehr viele Anwendungsfälle übrig.
Mr_E

1
@ Mr_E stimme zu, deshalb ist MongoDB ein bisschen dumm :)
Alexander Mills

6

Dies ist wahrscheinlich der beste Blog, den ich bezüglich der Implementierung einer transaktionsähnlichen Funktion für Mongodb gefunden habe.!

Synchronisierungsflag: Am besten nur zum Kopieren von Daten aus einem Masterdokument

Job Queue: sehr allgemeiner Zweck, löst 95% der Fälle. Die meisten Systeme müssen ohnehin mindestens eine Jobwarteschlange haben!

Zwei-Phasen-Commit: Diese Technik stellt sicher, dass jede Entität immer über alle Informationen verfügt, die erforderlich sind, um einen konsistenten Zustand zu erreichen

Protokollabstimmung: Die robusteste Technik, ideal für Finanzsysteme

Versionierung: Bietet Isolation und unterstützt komplexe Strukturen

Lesen Sie dies für weitere Informationen: https://dzone.com/articles/how-implement-robust-and


Bitte fügen Sie die relevanten Teile der verknüpften Ressource, die zur Beantwortung der Frage benötigt werden, in Ihre Antwort ein. Wie es ist, ist Ihre Antwort sehr anfällig für Linkfäule (dh wenn die verlinkte Website ausfällt oder sich ändert, ist Ihre Antwort möglicherweise nutzlos).
Mech

Danke @mech für den Vorschlag
Vaibhav

4

Dies ist spät, aber ich denke, dies wird in Zukunft helfen. Ich benutze Redis, um eine Warteschlange zu erstellen , um dieses Problem zu lösen.

  • Anforderung: Das folgende
    Bild zeigt, dass 2 Aktionen gleichzeitig ausgeführt werden müssen, Phase 2 und Phase 3 von Aktion 1 jedoch vor dem Start von Phase 2 von Aktion 2 oder umgekehrt beendet werden müssen (Eine Phase kann eine Anforderungs-REST-API, eine Datenbankanforderung oder die Ausführung von Javascript-Code sein ... ). Geben Sie hier die Bildbeschreibung ein

  • Wie eine Warteschlange Ihnen hilft Warteschlange Stellen Sie
    sicher, dass nicht jeder Blockcode zwischen lock()und release()in vielen Funktionen gleichzeitig ausgeführt wird. Isolieren Sie sie.

    function action1() {
      phase1();
      queue.lock("action_domain");
      phase2();
      phase3();
      queue.release("action_domain");
    }
    
    function action2() {
      phase1();
      queue.lock("action_domain");
      phase2();
      queue.release("action_domain");
    }
  • So erstellen Sie eine Warteschlange
    Ich werde mich nur darauf konzentrieren, wie Sie beim Erstellen einer Warteschlange auf der Backend-Site einen Teil der Race-Bedingungen vermeiden . Wenn Sie die Grundidee der Warteschlange nicht kennen, kommen Sie hierher .
    Der folgende Code zeigt nur das Konzept, das Sie korrekt implementieren müssen.

    function lock() {
      if(isRunning()) {
        addIsolateCodeToQueue(); //use callback, delegate, function pointer... depend on your language
      } else {
        setStateToRunning();
        pickOneAndExecute();
      }
    }
    
    function release() {
      setStateToRelease();
      pickOneAndExecute();
    }

Aber du musst dich selbst isRunning() setStateToRelease() setStateToRunning()isolieren, sonst bist du wieder mit Rennbedingungen konfrontiert. Dazu wähle ich Redis für ACID- Zwecke und skalierbar.
Redis Dokument spricht über die Transaktion:

Alle Befehle in einer Transaktion werden serialisiert und nacheinander ausgeführt. Es kann niemals vorkommen, dass eine von einem anderen Client ausgegebene Anforderung während der Ausführung einer Redis-Transaktion bearbeitet wird. Dies garantiert, dass die Befehle als einzelne isolierte Operation ausgeführt werden.

P / s:
Ich verwende Redis, da mein Dienst es bereits verwendet. Sie können dazu auch die Isolation auf andere Weise unterstützen.
Das action_domainin meinem Code ist oben für, wenn Sie nur Aktion 1 Aufruf von Benutzer A benötigen Blockieren Sie Aktion 2 von Benutzer A, blockieren Sie keinen anderen Benutzer. Die Idee ist, einen eindeutigen Schlüssel für die Sperre jedes Benutzers zu setzen.


Sie hätten mehr Upvotes erhalten, wenn Ihre Punktzahl bereits höher gewesen wäre. So denken die meisten hier. Ihre Antwort ist im Kontext der Frage hilfreich. Ich habe dich gestimmt.
Mukus

3

Transaktionen sind ab sofort in MongoDB 4.0 verfügbar. Probe hier

// Runs the txnFunc and retries if TransientTransactionError encountered

function runTransactionWithRetry(txnFunc, session) {
    while (true) {
        try {
            txnFunc(session);  // performs transaction
            break;
        } catch (error) {
            // If transient error, retry the whole transaction
            if ( error.hasOwnProperty("errorLabels") && error.errorLabels.includes("TransientTransactionError")  ) {
                print("TransientTransactionError, retrying transaction ...");
                continue;
            } else {
                throw error;
            }
        }
    }
}

// Retries commit if UnknownTransactionCommitResult encountered

function commitWithRetry(session) {
    while (true) {
        try {
            session.commitTransaction(); // Uses write concern set at transaction start.
            print("Transaction committed.");
            break;
        } catch (error) {
            // Can retry commit
            if (error.hasOwnProperty("errorLabels") && error.errorLabels.includes("UnknownTransactionCommitResult") ) {
                print("UnknownTransactionCommitResult, retrying commit operation ...");
                continue;
            } else {
                print("Error during commit ...");
                throw error;
            }
       }
    }
}

// Updates two collections in a transactions

function updateEmployeeInfo(session) {
    employeesCollection = session.getDatabase("hr").employees;
    eventsCollection = session.getDatabase("reporting").events;

    session.startTransaction( { readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } );

    try{
        employeesCollection.updateOne( { employee: 3 }, { $set: { status: "Inactive" } } );
        eventsCollection.insertOne( { employee: 3, status: { new: "Inactive", old: "Active" } } );
    } catch (error) {
        print("Caught exception during transaction, aborting.");
        session.abortTransaction();
        throw error;
    }

    commitWithRetry(session);
}

// Start a session.
session = db.getMongo().startSession( { mode: "primary" } );

try{
   runTransactionWithRetry(updateEmployeeInfo, session);
} catch (error) {
   // Do something with error
} finally {
   session.endSession();
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.