2012-06-18 18 views
22

Ich weiß, dass ich ein einzelnes Mongodb-Dokument nicht sperren kann, in der Tat gibt es auch keine Möglichkeit, eine Sammlung zu sperren.Es ist nicht möglich, ein Mongodb-Dokument zu sperren. Was, wenn ich muss?

Allerdings habe ich dieses Szenario, wo ich denke, ich brauche einen Weg, um mehr als einen Thread (oder Prozess, es ist nicht wichtig) daran, ein Dokument zu ändern. Hier ist mein Szenario.

Ich habe eine Sammlung, die Objekt des Typs A enthält. Ich habe Code, der ein Dokument des Typs A abrufen, ein Element in einem Array hinzufügen, das eine Eigenschaft des Dokuments ist (a.arr.add(new Thing()) und dann das Dokument speichern Mongodb. Dieser Code ist parallel, mehrere Threads in meinen Anwendungen können diese Operationen ausführen, und im Moment gibt es keine Möglichkeit zu verhindern, dass Threads diese Operationen parallel im selben Dokument ausführen. Das ist schlecht, weil einer der Threads die Werke des anderen überschreiben könnte.

Ich verwende das Repository-Muster, um den Zugriff auf die Mongodb-Sammlung zu abstrahieren, so dass ich nur CRUDs-Operationen zur Verfügung habe.

Jetzt, da ich darüber nachdenke, ist es vielleicht eine Einschränkung des Repository-Musters und keine Einschränkung von mongodb, die mir Probleme bereitet. Wie auch immer, wie kann ich diesen Code "threadsicher" machen? Ich denke, es gibt eine wohlbekannte Lösung für dieses Problem, aber da ich neu in mongodb und dem Repository-Muster bin, sehe ich es nicht sofort.

Dank

Antwort

4

in dieser Frage gestolpert während der Arbeit an mongodb Upgrades. Anders als zum Zeitpunkt, als diese Frage gestellt wurde, unterstützt mongodb jetzt das Sperren auf Dokumentebene.

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

„Wie granular sind Schlösser in MongoDB

in Version geändert 3.0

Ab Version 3.0, MongoDB Schiffe mit der WiredTiger Speicher-Engine, die eine Steuerung Parallelität verwendet?. für die meist Lese- und Schreiboperationen. WiredTiger verwendet nur Absichtssperren auf globale, Datenbank und Sammlung Ebenen. Wenn die Speicher-Engine Konflikte zwischen zwei Operationen erkennen, wird man einen Schreibkonflikt verursacht MongoDB entsteht transparent, dass die Operation zu wiederholen.“

+3

Was ist, wenn ich während der Erstellung des Dokumentobjekts sperren möchte? – Chinni

6

"Doktor, es tut weh, wenn ich diese tun"

"do Dann das nicht!"

Grundsätzlich klingt das, was Sie beschreiben, dass Sie eine serielle Abhängigkeit haben - MongoDB oder was auch immer, Ihr Algorithmus hat einen Punkt, an dem die Operation serialisiert werden muss. Das wird ein innewohnender Engpass sein, und wenn Sie es unbedingt tun müssen, müssen Sie eine Art Semaphor einrichten, um es zu schützen.

Also, der Ort zu suchen ist an Ihrem Algorithmus. Kannst du das beseitigen? Könnten Sie zum Beispiel mit einer Art von Konfliktlösung umgehen, wie zum Beispiel "lokale Aktualisierung aufnehmen", "Datensatz speichern", so dass nach dem Laden der neue Datensatz derjenige ist, der auf diesem Schlüssel gefunden wurde?

+0

ich Charlie, vielen Dank für die Beantwortung. Ich verstehe die von Ihnen vorgeschlagene Konfliktlösung nicht. Ich stimme zu, dass ich meinen Algorithmus ändern muss, und ich kann mir eine Lösung vorstellen, aber ich denke, dass es eine einvernehmliche Lösung für dieses Problem geben muss. Es scheint mir, dass es ein klassisches Problem ist, dass viele Leute, die mongodb (oder wahrscheinlich irgendeine Datenbank) benutzen, in dieses Problem geraten sind. Wenn es sich um eine im Speicher-Update war, würde ich wissen, wie ein Mutex verwenden, um „sperren“ die Variable I so aktualisieren möchten nur ein Thread es zu einem Zeitpunkt, aktualisieren. Ich denke, meine Frage ist: Wie gehen andere Programmierer normalerweise mit dieser Situation um? –

12

Hey die einzige Möglichkeit, von der ich jetzt denke, ist, einen Statusparameter hinzuzufügen und die Operation findAndModify() zu verwenden, die es ermöglicht, ein Dokument atomar zu ändern. Es ist ein bisschen langsamer, sollte aber den Trick machen.

Nehmen wir an, Sie fügen ein Statusattribut hinzu, und wenn Sie das Dokument abrufen, ändern Sie den Status von "IDLE" in "PROCESSING". Anschließend aktualisieren Sie das Dokument und speichern es in der Sammlung, wobei der Status erneut auf "IDLE" aktualisiert wird.

Code-Beispiel:

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

Ändern der COLLECTION_NAME und ID_DOCUMENT auf einen geeigneten Wert. Standardmäßig gibt findAndModify() den alten Wert zurück, was bedeutet, dass der Statuswert auf der Clientseite immer noch IDLE ist. Wenn Sie mit dem Update fertig sind, speichern/aktualisieren Sie alles erneut.

Denken Sie nur daran, dass Sie nur ein Dokument gleichzeitig ändern können.

Ich hoffe, es hilft.

+0

Sie können mit einfachen update() für den gleichen Zweck, das die offizielle Lösung bei MongoDB-Website angeboten wird: http://docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations/ Die wichtigste Komplikation Dieser Code muss jedoch für den Fall geschrieben werden, wenn das Update fehlschlägt. I.e. Wiederholen Sie das Update. Abhängig von Ihrem Code können Sie in weitere Komplikationen laufen Nebenwirkungen zu vermeiden, wenn erneut versucht, usw. –

+0

Wie ein anderer Client warten, bis die Sperre aufgehoben werden? Wie können Sie benachrichtigt werden, wenn sich 'Status' ändert? – slezica

+0

Was ist, wenn ich während der Erstellung des Dokumentobjekts sperren möchte? – Chinni

2

Beantworten meiner eigenen Frage, weil ich eine Lösung während der Forschung im Internet gefunden habe.

Ich denke, was ich tun muss, ist ein Optimistic Concurency Control verwenden.

Es besteht darin, zu jedem Dokument einen Zeitstempel, einen Hash oder eine andere eindeutige Kennung (I UUIDs) hinzuzufügen. Der eindeutige Bezeichner muss jedes Mal geändert werden, wenn das Dokument geändert wird. bevor das Dokument zu aktualisieren so etwas wie dies Ich werde (in Pseudo-Code) tun:

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(); 
} 
+0

Yup, das ist eine Methode der Konfliktlösung. –

+0

Sie können das tun, aber einige der anderen Antworten, die die atomaren Operatoren beschreiben, ist wahrscheinlich das, was Sie wollen (und ist atomar wie Sie wollen). Hier sind die Dokumente: http://www.mongodb.org/display/DOCS/Atomic+Operations – will

-1

Klingt wie Sie MongoDB Atombetreiber verwenden möchten: http://www.mongodb.org/display/DOCS/Atomic+Operations

+0

Das Problem mit den atomaren Operatoren ist, dass sie mir nicht wirklich helfen, da ich die Repository-Muster verwendet habe, also hatte ich nur CRUD-Operationen zu meiner Verfügung. –

3

Klassische Lösung, wenn Sie etwas Thread-sicher machen wollen, ist Schlösser zu verwenden (Mutex). Dies wird auch pessimistische Verriegelung im Gegensatz zu optimistischen Sperren beschrieben here.

Es gibt Szenarien, in denen pessimistisches Sperren effizienter ist (mehr Details here). Es ist auch viel einfacher zu implementieren (Hauptschwierigkeit des optimistischen Sperrens ist die Erholung von der Kollision).

MongoDB bietet keinen Mechanismus für eine Sperre. Aber dies kann leicht auf Anwendungsebene (dh in Ihrem Code) realisiert werden:

  1. Acquire Schloss
  2. Dokument lesen
  3. ändern Dokument
  4. schreiben Dokument
  5. Entriegelungsschloss

Die Granularität der Sperre kann unterschiedlich sein: global, sammlungspezifisch, satz-/dokumentenspezifisch. Je spezifischer die Sperre ist, desto geringer ist ihre Leistungseinbuße.

+0

Wie warten Sie auf das Schloss? – slezica

+0

Acquire lock Aktion wartet normalerweise auf eine Sperre, wenn sie von einem anderen Thread gehalten wird. –

+5

Dies funktioniert nicht in einer Anwendung mit mehreren Instanzen. – rickchristie

0

Wenn Sie ein System mit> 1 Servern haben, benötigen Sie eine distributive Sperre.

Ich bevorzuge die Verwendung Hazelcast.

Während des Speicherns können Sie die Hazelcast-Sperre nach Entitäts-ID abrufen, Daten abrufen und aktualisieren und dann eine Sperre aufheben.

Als Beispiel: https://github.com/azee/template-api/blob/master/template-rest/src/main/java/com/mycompany/template/scheduler/SchedulerJob.java

Nur lock.lock() anstelle von lock.tryLock()

Hier können Sie sehen, wie Hazelcast im Frühjahr Kontext konfigurieren:

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

1

Update: mit MongoDB 3.2.2 WiredTiger Speichern Implementierung als Standard-Engine verwenden, Standard MongoDB Verwendung Verriegelung am Dokument level.It wurde in Version 3.0 eingeführt, aber gemacht Standard in der Version 3.2.2. Daher verfügt MongoDB jetzt über eine Sperre auf Dokumentebene.

0

Statt die Frage in einer anderen Frage zu schreiben, ich versuche, diese zu beantworten: Ich frage mich, ob diese WiredTiger Lagerung wird das Problem behandelt ich hier darauf hingewiesen: Limit inserts in mongodb

Verwandte Themen