Wie man Transaktionen in MongoDB jetzt fühlt

Im Sommer 2018 (also zum Zeitpunkt dieses Schreibens) geschah das Unglaubliche - ehrliche ACID-Transaktionen wurden in MongoDB eingebracht. Mit der Veröffentlichung der vierten Version dieses dokumentenorientierten DBMS kann es für etwas ernstere Anwendungen verwendet werden.


Für diejenigen, die sich im Tank befinden, kurz gesagt: Transaktionen ermöglichen es uns, eine Reihe von Änderungen an mehreren Dokumenten vorzunehmen und diese gleichzeitig zu speichern oder alle innerhalb der Transaktion vorgenommenen Änderungen abzubrechen, wenn ein Fehler aufgetreten ist oder die Anwendung abgestürzt ist .


Leider ist es für den Entwickler nicht so einfach, diese Superfunktion zu verwenden. Im Folgenden werde ich Ihnen erklären, warum und was Sie dagegen tun müssen.


Wenn wir die Dokumentation für das DBMS im Abschnitt Transaktionen öffnen, sehen wir die folgende Bemerkung:


Transaktionen mit mehreren Dokumenten sind nur für Replikatsätze verfügbar. Transaktionen für Sharded-Cluster sind für MongoDB 4.2 geplant

Dies zeigt uns, dass ein einfacher MongoDB-Server keine Transaktionen unterstützt, sondern nur einen Cluster im Replikatsatzmodus . Die Unterstützung für Sharded- Cluster wird auch später in Version 4.2 verfügbar sein.


Gleichzeitig können wir auf einem normalen Server eine Transaktion starten, speichern oder abbrechen. Darin kann jedoch nichts unternommen werden. Ein Fehler wie dieser wird angezeigt:


WriteCommandError({ "ok" : 0, "errmsg" : "Transaction numbers are only allowed on a replica set member or mongos", "code" : 20, "codeName" : "IllegalOperation" }) 

Glücklicherweise kann jeder einen MongoDB-Cluster mit einem Server ausführen. Auf meinen Maschinen, auf denen ich mich mit der Entwicklung befasse, führe ich alle DBMS in Docker- Containern aus. Das Starten eines regulären MongoDB-Servers sieht beispielsweise folgendermaßen aus:


 docker run -v ~/mongo/:/data/db --name mongo --restart=always -p 27017:27017 -d mongo mongod --smallfiles 

Lassen Sie uns die Startschlüssel analysieren:


  • -v ~ / mongo /: / data / db bedeutet, dass das lokale Verzeichnis ~ / mongo / in / data / db des Containers bereitgestellt wird , sodass die Datenbank selbst auf dem Hostcomputer gespeichert wird, sodass wir den laufenden Container löschen, Versionen aktualisieren usw. können. d. unter Wahrung unserer Daten;
  • --name mongo legt den Namen des Containers fest;
  • --restart = sagt immer , dass im Falle eines Dienstabsturzes im Container der Container neu gestartet und der Container nach dem Laden des Betriebssystems gestartet werden sollte.
  • -p 27017: 27017 "leitet" den Port an den Host-Computer weiter;
  • -d gibt an, dass Sie den Container als Daemon starten müssen.
  • mongo - der Name des Bildes, auf dem der Container ausgeführt werden soll;
  • mongod --smallfiles - Befehl zum Starten des Dienstes im Container.

Wie man einen einfachen Server startet, habe ich nur als Referenz mitgebracht. Lassen Sie uns nun verstehen, was getan werden muss, um einen Server zu starten, der Transaktionen unterstützt.


Zunächst sollten Sie im Docker ein neues Netzwerk erstellen, in dem alle Server unseres Clusters funktionieren. Ja, ich habe oben geschrieben, dass es einen Server geben wird, aber ein Netzwerk muss erstellt werden, sonst funktioniert nichts.


 docker network create mongo-cluster 

Als Nächstes müssen Sie in den Container-Startparametern die Verwendung des neuen Netzwerks --net mongo-cluster angeben und den Parameter an den Server übergeben, um im Replikatsatzmodus zu arbeiten : --replSet rs0 . Außerdem habe ich absichtlich den Schalter --restart = immer weggelassen, als Ich verwende MongoDB im Moment nicht immer bei der Arbeit und möchte nicht, dass es mit dem Betriebssystem beginnt.


 docker run -v ~/mongo/:/data/db --name mongo -p 27017:27017 -d mongo mongod --smallfiles --replSet rs0 

Großartig, der Container wird ausgeführt, wie wir sehen können, wenn Sie den Befehl docker ps ausführen und Folgendes sehen:


 docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2292d7e0778b mongo "docker-entrypoint.s…" About a minute ago Up About a minute 0.0.0.0:27017->27017/tcp mongo 

Als nächstes müssen wir den Cluster initialisieren. Dazu gehen wir in die Konsole des laufenden Servers, erstellen die Konfiguration unseres Clusters und führen die Initialisierung durch:


 docker exec -it mongo mongo # output omited # > config = { "_id" : "rs0", "members" : [ { "_id" : 0, "host" : "mongo:27017" } ] } > rs.initiate(config) { "ok" : 1, "operationTime" : Timestamp(1531248932, 1), "$clusterTime" : { "clusterTime" : Timestamp(1531248932, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } } rs0:SECONDARY> rs0:PRIMARY> 

Fertig! Wir haben einen Cluster von einem MongoDB-Server. Jetzt können Sie überprüfen, ob alles wie erwartet funktioniert.


 rs0:PRIMARY> session = db.getMongo().startSession() session { "id" : UUID("7eb81006-983f-4398-adc7-5ed23e027377") } rs0:PRIMARY> database = session.getDatabase("test") test rs0:PRIMARY> //    rs0:PRIMARY> database.col.insert({name: "1"}) WriteResult({ "nInserted" : 1 }) rs0:PRIMARY> database.col.insert({name: "2"}) WriteResult({ "nInserted" : 1 }) rs0:PRIMARY> database.col.insert({name: "3"}) WriteResult({ "nInserted" : 1 }) rs0:PRIMARY> database.col.insert({name: "4"}) WriteResult({ "nInserted" : 1 }) rs0:PRIMARY> // ,     rs0:PRIMARY> database.col.find({}) { "_id" : ObjectId("5b45026edc396f534f11952f"), "name" : "1" } { "_id" : ObjectId("5b450272dc396f534f119530"), "name" : "2" } { "_id" : ObjectId("5b450274dc396f534f119531"), "name" : "3" } { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "4" } rs0:PRIMARY> //   rs0:PRIMARY> session.startTransaction() rs0:PRIMARY> //    rs0:PRIMARY> database.col.update({name: "4"}, {name: "44"}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) rs0:PRIMARY> //   rs0:PRIMARY> database.col.find({}) { "_id" : ObjectId("5b45026edc396f534f11952f"), "name" : "1" } { "_id" : ObjectId("5b450272dc396f534f119530"), "name" : "2" } { "_id" : ObjectId("5b450274dc396f534f119531"), "name" : "3" } { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "44" } rs0:PRIMARY> //         ,    -: rs0:PRIMARY> // { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "4" } rs0:PRIMARY> //   rs0:PRIMARY> session.commitTransaction() rs0:PRIMARY> //   rs0:PRIMARY> database.col.find({}) { "_id" : ObjectId("5b45026edc396f534f11952f"), "name" : "1" } { "_id" : ObjectId("5b450272dc396f534f119530"), "name" : "2" } { "_id" : ObjectId("5b450274dc396f534f119531"), "name" : "3" } { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "44" } rs0:PRIMARY> //     rs0:PRIMARY> session.startTransaction() rs0:PRIMARY> database.col.update({name: "44"}, {name: "42"}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) rs0:PRIMARY> database.col.find({}) { "_id" : ObjectId("5b45026edc396f534f11952f"), "name" : "1" } { "_id" : ObjectId("5b450272dc396f534f119530"), "name" : "2" } { "_id" : ObjectId("5b450274dc396f534f119531"), "name" : "3" } { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "42" } rs0:PRIMARY> database.col.update({name: "1"}, {name: "21"}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) rs0:PRIMARY> database.col.find({}) { "_id" : ObjectId("5b45026edc396f534f11952f"), "name" : "21" } { "_id" : ObjectId("5b450272dc396f534f119530"), "name" : "2" } { "_id" : ObjectId("5b450274dc396f534f119531"), "name" : "3" } { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "42" } rs0:PRIMARY> session.commitTransaction() rs0:PRIMARY> //   rs0:PRIMARY> database.col.find({}) { "_id" : ObjectId("5b45026edc396f534f11952f"), "name" : "21" } { "_id" : ObjectId("5b450272dc396f534f119530"), "name" : "2" } { "_id" : ObjectId("5b450274dc396f534f119531"), "name" : "3" } { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "42" } rs0:PRIMARY> //   ,     rs0:PRIMARY> session.startTransaction() rs0:PRIMARY> database.col.update({name: "21"}, {name: "1"}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) rs0:PRIMARY> database.col.find({}) { "_id" : ObjectId("5b45026edc396f534f11952f"), "name" : "1" } { "_id" : ObjectId("5b450272dc396f534f119530"), "name" : "2" } { "_id" : ObjectId("5b450274dc396f534f119531"), "name" : "3" } { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "42" } rs0:PRIMARY> //   rs0:PRIMARY> session.abortTransaction() rs0:PRIMARY> database.col.find({}) { "_id" : ObjectId("5b45026edc396f534f11952f"), "name" : "21" } { "_id" : ObjectId("5b450272dc396f534f119530"), "name" : "2" } { "_id" : ObjectId("5b450274dc396f534f119531"), "name" : "3" } { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "42" } rs0:PRIMARY> // !     ! rs0:PRIMARY> 

So können Sie Mong-Transaktionen jetzt ohne Anstrengung ausprobieren, ohne einen Multiserver-Cluster zu starten. Ich empfehle Ihnen, die Dokumentation zu lesen und sich über Transaktionsbeschränkungen zu informieren. Wenn Sie beispielsweise nicht in der Lage sind, die Änderungen zu speichern, werden diese Transaktionen abgebrochen.


PS: Der Zweck dieses Artikels ist nicht, Ihnen die Verwendung von Docker oder die Arbeit mit Monga beizubringen, sondern nur eine schnelle Möglichkeit, neue Tools dieses interessanten DBMS auszuprobieren.

Source: https://habr.com/ru/post/de417131/


All Articles