Il n'est pas possible de verrouiller un document mongodb. Que faire si j'ai besoin de?
je sais que je ne peux pas verrouiller un seul document mongodb, en fait il n'y a aucun moyen de verrouiller une collection non plus.
cependant, j'ai ce scénario, où je pense que j'ai besoin d'un moyen pour 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 un code que récupérer un document de type A, ajouter un élément dans un tableau qui est une propriété du document ( a.arr.add(new Thing()
) puis enregistrer le document à mongodb. Ce code est parallèle, plusieurs threads dans Mes applications peuvent faire ces opérations et pour l'instant il n'y a aucun moyen d'empêcher les threads de faire ces opérations en parallèle sur le même document. C'est mauvais parce que l'un des fils pourrait écraser les œuvres de l'autre.
j'utilise le motif du dépôt pour abstraire l'accès à la collection mongodb, donc je n'ai que des CRUDs les opérations à ma disposition.
maintenant que j'y pense, peut-être que c'est une limitation du schéma du dépôt et non une limitation de mongodb qui me cause des problèmes. De toute façon, comment puis-je rendre ce code "thread safe"? Je suppose qu'il y a une solution bien connue à ce problème, mais étant nouveau à mongodb et le modèle de dépôt, Je ne le vois pas immédiatement.
Merci
11 réponses
est tombé dans cette question tout en travaillant sur les mises à niveau mongodb. Contrairement à l'époque où cette question a été posée, maintenant mongodb soutient le niveau de document verrouillant hors de la boîte.
de: http://docs.mongodb.org/manual/faq/concurrency /
"Comment granulés sont des verrous dans MongoDB?
modifié dans la version 3.0.
à partir de la version 3.0, les navires MongoDB avec le stockage WiredTiger moteur, qui utilise le contrôle de concurrence optimiste pour la plupart des opérations de lecture et d'écriture. WiredTiger utilise uniquement des serrures intentionnelles aux niveaux global, de la base de données et de la collection. Lorsque le moteur de stockage détecte des conflits entre deux opérations, L'une d'entre elles provoque un conflit en écriture, ce qui amène MongoDB à réessayer cette opération de manière transparente."
Hey 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 atomiquement un document. C'est un peu plus lent, mais devrait faire l'affaire.
alors disons que vous ajoutez un attribut de statut et que lorsque vous récupérez le document, changez le statut de" IDLE "en"PROCESSING". Puis vous mettez à jour le document et le sauvegardez à nouveau dans la collection de mise à jour de l'État à "IDLE" de nouveau.
exemple de Code:
var doc = db.runCommand({
"findAndModify" : "COLLECTION_NAME",
"query" : {"_id": "ID_DOCUMENT", "status" : "IDLE"},
"update" : {"$set" : {"status" : "RUNNING"} }
}).value
changez la valeur COLLECTION_NAME et ID_DOCUMENT pour une valeur correcte. Par défaut, findAndModify () renvoie l'ancienne valeur, ce qui signifie que la valeur d'état sera toujours inutilisée du côté du client. Ainsi, lorsque vous avez terminé la mise à jour, sauvegardez/mettez tout à jour à nouveau.
La seule chose que vous devez être conscient, c'est que vous ne pouvez modifier un document à la fois.
J'espère que ça aidera.
"Docteur, ça fait mal quand je fais ce "
"ne pas faire !"
En Gros, ce que vous décrivez ressemble à une dépendance de série -- MongoDB ou autre, votre algorithme a un point à partir duquel l'opération doit être sérialisée. Ce sera un inhérente goulot d'étranglement, et si vous devez absolument le faire, vous aurez à organiser une sorte de sémaphore pour le protéger.
donc, l'endroit à regarder est votre algorithme. Tu peux éliminer ça? Pourriez-vous, par exemple, gérer cela avec une sorte de résolution de conflit, comme "get record into local' update; store record" de sorte qu'après le magasin le nouveau record serait celui obtenu sur cette clé?
solution classique quand vous voulez faire quelque chose fil-sûre est d'utiliser des serrures (mutex). Il est également appelé verrouillage pessimiste par opposition à verrouillage optimiste décrit ici .
il y a des scénarios où le verrouillage pessimiste est plus efficace (plus de détails ici ). Il est également beaucoup plus facile à mettre en œuvre (difficulté majeure de verrouillage optimiste est la récupération de collision.)
MongoDB ne prévoit pas de mécanisme pour une serrure. Mais cela peut être facilement implémenté au niveau de l'application (i.e. dans votre code):
- Acquérir verrouillage 1519160920"
- Lire document
- modifier le document
- document d'Écriture
- Libération
La granularité de la serrure peut être différente: global, collection spécifique, enregistrement de document spécifique. Plus le cadenas est précis, moins il est pénalisé.
répondre à ma propre question parce que j'ai trouvé une solution tout en faisant des recherches sur Internet.
je pense que ce dont j'ai besoin pour faire est d'utiliser un Optimiste Concurency de Contrôle .
cela consiste à ajouter un horodatage, un hachage ou un autre identifiant unique (j'utiliserai UUIDs) à 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 ceci (dans 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();
}
une alternative est de faire en place mise à jour
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 tout le tableau d'inventaire de L'utilisateur" Sourcerer". Mise à jour de chaque document/utilisateur est atomique.
mise à Jour: Avec MongoDB 3.2.2 en utilisant WiredTiger Storage implementation comme moteur par défaut, MongoDB utilise le verrouillage par défaut au niveau du document.Il a été introduit dans la version 3.0 mais est devenu par défaut dans la version 3.2.2. Par conséquent MongoDB a maintenant le niveau de verrouillage des documents.
si vous avez un système avec > 1 Serveurs, alors vous aurez besoin d'un verrou distributif.
je préfère utiliser Hazelcast .
tout en sauvegardant vous pouvez obtenir la serrure Hazelcast par l'identifiant de l'entité, récupérer et mettre à jour les données, puis libérer une serrure.
comme 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
au lieu d'écrire la question dans une autre question, j'essaie de répondre à celle-ci: je me demande si ce stockage WiredTiger va gérer le problème que j'ai souligné ici: Inserts limites en mongodb
si l'ordre des éléments dans le tableau n'est pas important pour vous, alors l'opérateur $push devrait être assez sûr pour empêcher les threads de se surcharger les uns les autres Change.
sonne comme si vous vouliez utiliser les opérateurs atomiques de MongoDB: http://www.mongodb.org/display/DOCS/Atomic+opérations