À l'été 2018 (c'est-à-dire en ce moment, au moment d'écrire ces lignes), l'incroyable s'est produit - des transactions ACID honnêtes ont été introduites dans MongoDB . Avec la sortie de la quatrième version de ce SGBD orienté document, il peut être utilisé pour des applications légèrement plus sérieuses.
Pour ceux qui sont dans le réservoir, en bref: les transactions nous permettent d'effectuer une série de modifications dans plusieurs documents et de les enregistrer en même temps, ou d'annuler toutes les modifications apportées au sein de la transaction de la même manière en cas de problème ou de plantage de l'application .
Malheureusement, le développeur n'est pas si facile à utiliser cette super fonctionnalité. Ci-dessous, je vais vous dire pourquoi et quoi faire à ce sujet.
Si nous ouvrons la documentation du SGBD dans la section Transactions , nous pouvons voir la remarque suivante:
Les transactions multi-documents sont disponibles uniquement pour les jeux de réplicas. Les transactions pour les clusters fragmentés sont planifiées pour MongoDB 4.2
Cela nous indique qu'un simple serveur MongoDB ne prend pas en charge les transactions, uniquement un cluster en mode jeu de réplicas . La prise en charge des clusters fragmentés viendra également plus tard dans la version 4.2.
Dans le même temps, un serveur normal nous permettra de démarrer une transaction, de l'enregistrer ou de l'annuler, mais rien ne peut y être fait, une erreur comme celle-ci s'affichera:
WriteCommandError({ : 0, : , : 20, : })
Heureusement, n'importe qui peut exécuter un cluster MongoDB à serveur unique. Sur mes machines, sur lesquelles je suis engagé dans le développement, j'exécute tous les SGBD dans des conteneurs dockers . Par exemple, le démarrage d'un serveur MongoDB normal ressemble à ceci:
docker run -v ~/mongo/:/data/db --name mongo --restart=always -p 27017:27017 -d mongo mongod --smallfiles
Analysons les clés de démarrage:
- -v ~ / mongo /: / data / db signifie monter le répertoire local ~ / mongo / in / data / db du conteneur, de sorte que la base de données elle-même sera stockée sur la machine hôte, ce qui nous permettra de supprimer le conteneur en cours d'exécution, de mettre à jour les versions, etc. d. avec la conservation de nos données;
- --name mongo définit le nom du conteneur;
- --restart = indique toujours qu'en cas de panne de service dans le conteneur, il doit être redémarré, ainsi que le démarrage du conteneur après le chargement du système d'exploitation;
- -p 27017: 27017 "transfère" le port à la machine hôte;
- -d indique que vous devez démarrer le conteneur en tant que démon;
- mongo - le nom de l'image pour exécuter le conteneur;
- mongod --smallfiles - commande pour démarrer le service dans le conteneur.
Comment démarrer un serveur simple, je l'ai apporté juste pour référence. Maintenant, comprenons ce qui doit être fait pour démarrer un serveur qui prend en charge les transactions.
Tout d'abord, vous devez créer un nouveau réseau à l'intérieur du docker, dans lequel tous les serveurs de notre cluster fonctionneront. Oui, j'ai écrit ci-dessus qu'il y aura un serveur, mais un réseau doit être créé, sinon rien ne fonctionnera.
docker network create mongo-cluster
Ensuite, dans les paramètres de lancement du conteneur, vous devez spécifier l'utilisation du nouveau réseau --net mongo-cluster , et également transmettre le paramètre au serveur pour travailler en mode de jeu de réplicas: --replSet rs0 . De plus, j'ai intentionnellement omis le commutateur --restart = always , car Je n'utilise pas toujours MongoDB au travail pour le moment et je ne veux pas qu'il démarre avec le système d'exploitation.
docker run -v ~/mongo/:/data/db --name mongo -p 27017:27017 -d mongo mongod --smallfiles --replSet rs0
Très bien, le conteneur est en cours d'exécution, comme nous pouvons le voir en exécutant la commande docker ps et en voyant quelque chose comme ceci:
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
Ensuite, nous devons initialiser le cluster, pour cela nous allons dans la console du serveur en cours d'exécution, créons la configuration de notre cluster et effectuons l'initialisation:
docker exec -it mongo mongo
C'est fait! Nous avons obtenu un cluster d'un serveur MongoDB. Vous pouvez maintenant vérifier que tout fonctionne comme prévu.
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>
Ainsi, sans forcer, vous pouvez essayer les transactions Mong dès maintenant sans démarrer un cluster multiserveur. Je vous conseille de consulter la documentation et de lire les restrictions de transaction. Par exemple, si les transactions ne "vivent" pas plus d'une minute, si vous n'arrivez pas à enregistrer les modifications, elles seront annulées.
PS: le but de cet article n'est pas de vous apprendre à utiliser docker ou à travailler avec monga, mais seulement un moyen rapide d'essayer de nouveaux outils de ce SGBD intéressant.