web-dev-qa-db-fra.com

Il n'est pas possible de verrouiller un document mongodb. Et si j'en ai besoin?

Je sais que je ne peux pas verrouiller un seul document mongodb. En fait, il n’existe aucun moyen de verrouiller une collection.

Cependant, j'ai ce scénario dans lequel je pense avoir besoin d'un moyen d'empêcher plus d'un thread (ou processus, ce n'est pas important) de modifier un document. Voici mon scénario.

J'ai une collection qui contient un objet de type A. J'ai du code qui extrait un document de type A, ajoute un élément dans un tableau qui est une propriété du document (a.arr.add(new Thing()), puis enregistre le document dans mongodb. Ce code est parallèle, plusieurs threads de mes applications peuvent effectuer ces opérations et pour l’instant, il n’existe aucun moyen d’empêcher les threads d’effectuer ces opérations en parallèle sur le même document. C'est mauvais parce que l'un des threads pourrait écraser les travaux de l'autre.

J'utilise le modèle de référentiel pour résumer l'accès à la collection mongodb, je n'ai donc que les opérations CRUD à ma disposition.

Maintenant que j'y pense, c'est peut-être une limitation du modèle de référentiel et non une limitation de mongodb qui me pose problème. Quoi qu'il en soit, comment puis-je rendre ce code "thread safe"? Je suppose qu'il existe une solution bien connue à ce problème, mais étant nouveau pour mongodb et le modèle de référentiel, je ne le vois pas immédiatement.

Merci

27
Mathieu Pagé

Nous sommes tombés sur cette question alors que nous travaillions sur les mises à niveau de mongodb. Contrairement à l'époque où cette question a été posée, mongodb prend désormais en charge le verrouillage immédiat du niveau de document.

De: http://docs.mongodb.org/manual/faq/concurrency/

"Dans quelle mesure les verrous dans MongoDB sont-ils granulaires?

Modifié dans la version 3.0.

À partir de la version 3.0, MongoDB est livré avec le moteur de stockage WiredTiger, qui utilise un contrôle de concurrence optimiste pour la plupart des opérations de lecture et d'écriture. WiredTiger utilise uniquement des verrous d'intention aux niveaux global, base de données et collection. Lorsque le moteur de stockage détecte des conflits entre deux opérations, un conflit d’écriture se produit, ce qui oblige MongoDB à réessayer de manière transparente cette opération. "

7
Mahesh

Hé, la seule façon dont je pense maintenant est d’ajouter un paramètre de statut et d’utiliser l’opération findAndModify () , qui vous permet de modifier le document de façon atomique. C'est un peu plus lent, mais devrait faire l'affaire.

Supposons donc que vous ajoutez un attribut d'état et que, lorsque vous récupérez le document, changez l'état "IDLE" en "TRAITEMENT". Ensuite, vous mettez à jour le document et enregistrez-le dans la collection en mettant à nouveau le statut à "IDLE".

Exemple de code:

var doc = db.runCommand({
              "findAndModify" : "COLLECTION_NAME",
              "query" : {"_id": "ID_DOCUMENT", "status" : "IDLE"},
              "update" : {"$set" : {"status" : "RUNNING"} }
}).value

Remplacez COLLECTION_NAME et ID_DOCUMENT par une valeur appropriée. Par défaut, findAndModify () renvoie l'ancienne valeur, ce qui signifie que la valeur du statut sera toujours IDLE du côté client. Ainsi, lorsque vous avez terminé la mise à jour, il vous suffit de sauvegarder/mettre à jour le tout à nouveau.

La seule chose dont vous devez être conscient, c'est que vous ne pouvez modifier qu'un document à la fois.

J'espère que ça aide.

12
golja

"Docteur, ça fait mal quand je fais ceci"

"Alors ne fais pas {fait ça!"

Fondamentalement, ce que vous décrivez ressemble à une dépendance série - MongoDB ou autre, votre algorithme a un point auquel l'opération doit être sérialisée. Ce sera un goulot d'étranglement inhérent, et si vous devez absolument le faire, vous devrez disposer d'une sorte de sémaphore pour le protéger.

Donc, l'endroit à regarder est votre algorithme. Pouvez-vous éliminer cela? Pourriez-vous, par exemple, gérer cela avec une sorte de résolution de conflit, comme "enregistrer dans une mise à jour locale; stocker un enregistrement" afin qu'après le stockage, le nouvel enregistrement soit celui obtenu sur cette clé?

5
Charlie Martin

La solution classique lorsque vous souhaitez créer un environnement thread-safe consiste à utiliser des verrous (mutex). Ceci est aussi appelé verrouillage pessimiste par opposition à verrouillage optimiste décrit ici .

Il existe des scénarios dans lesquels le verrouillage pessimiste est plus efficace (plus de détails ici ). Il est également beaucoup plus facile à mettre en œuvre (la principale difficulté du verrouillage optimiste est le rétablissement après une collision).

MongoDB ne fournit pas de mécanisme pour un verrou. Mais cela peut être facilement implémenté au niveau de l’application (c’est-à-dire dans votre code):

  1. Acquérir un verrou
  2. Lire le document
  3. Modifier le document
  4. Écrire un document
  5. Déverrouiller

La granularité du verrou peut être différente: globale, spécifique à la collection, spécifique à l'enregistrement/au document. Plus le verrou est spécifique, moins sa performance est pénalisée.

3

Une alternative est de faire mise à jour sur place

pour ex: 

http://www.mongodb.org/display/DOCS/Updating#comment-41821928

db.users.update( { level: "Sourcerer" }, { '$Push' : { 'inventory' : 'magic wand'} }, false, true );

qui va pousser «baguette magique» dans tous les tableaux d'inventaire de l'utilisateur "Sourcerer". La mise à jour de chaque document/utilisateur est atomique. 

2
Prashant Bhate

Mise à jour: Avec MongoDB 3.2.2 qui utilise l’implémentation de WiredTiger Storage en tant que moteur par défaut, MongoDB utilise le verrouillage par défaut au niveau du document. Par conséquent, MongoDB dispose maintenant d’un verrouillage au niveau du document. 

2
Satyam

Répondant à ma propre question parce que j'ai trouvé une solution en faisant des recherches sur Internet.

Je pense que ce que je dois faire est d’utiliser un Optimistic Concurency Control .

Cela consiste à ajouter un horodatage, un hachage ou un autre identifiant unique (je vais utiliser des UUID) à chaque document. L'identifiant unique doit être modifié chaque fois que le document est modifié. avant de mettre à jour le document, je vais faire quelque chose comme ça (en pseudo-code):

var oldUUID = doc.uuid;
doc.uuid = new UUID();
BeginTransaction();
if (GetDocUUIDFromDatabase(doc.id) == oldUUID)
{
   SaveToDatabase(doc);
   Commit();
}
else
{
   // Document was modified in the DB since we read it. We can't save our changes.
   RollBack();
   throw new ConcurencyException();
}
2
Mathieu Pagé

Si l'ordre des éléments dans le tableau n'est pas important pour vous, l'opérateur $ Push doit être suffisamment sûr pour empêcher les threads d'écraser les modifications apportées.

0
Abdelrahman Hossam

Si vous avez un système avec> 1 serveurs, vous aurez besoin d'un verrou distributif. 

Je préfère utiliser Hazelcast

Lors de la sauvegarde, vous pouvez obtenir un verrou Hazelcast par ID d’entité, récupérer et mettre à jour des données, puis libérer un verrou.

Par exemple: https://github.com/azee/template-api/blob/master/template-rest/src/main/Java/com/mycompany/template/scheduler/SchedulerJob.Java

Il suffit d'utiliser lock.lock() au lieu de lock.tryLock()

Ici vous pouvez voir comment configurer Hazelcast dans votre contexte de printemps:

https://github.com/azee/template-api/blob/master/template-rest/src/main/resources/webContext.xml

0
Azee

Depuis la version 4.0, MongoDB prend en charge Transactions pour les jeux de réplicas. La prise en charge des clusters fragmentés viendra dans MongoDB 4.2. A l'aide de transactions, les mises à jour de base de données seront annulées si une écriture en conflit se produit, résolvant votre problème. 

Les performances sont beaucoup plus coûteuses en termes de performances. N'utilisez donc pas Transactions comme excuse pour la mauvaise conception de schéma NoSQL!

0
Govind Rai

Au lieu d’écrire la question dans une autre question, j’essaie de répondre à celle-ci: je me demande si ce stockage WiredTiger résoudra le problème que j’ai signalé ici: Limit insert dans mongodb

0
oderfla