Nein. CouchDB verwendet ein "optimistisches Parallelitäts" -Modell. Im einfachsten Sinne bedeutet dies nur, dass Sie eine Dokumentversion zusammen mit Ihrem Update senden und CouchDB die Änderung ablehnt, wenn die aktuelle Dokumentversion nicht mit der von Ihnen gesendeten übereinstimmt.
Es ist wirklich täuschend einfach. Sie können viele normale transaktionsbasierte Szenarien für CouchDB neu gestalten. Sie müssen jedoch Ihr RDBMS-Domänenwissen irgendwie wegwerfen, wenn Sie CouchDB lernen. Es ist hilfreich, Probleme von einer höheren Ebene aus anzugehen, anstatt zu versuchen, Couch an eine SQL-basierte Welt anzupassen.
Inventar verfolgen
Das von Ihnen beschriebene Problem ist in erster Linie ein Inventarproblem. Wenn Sie ein Dokument haben, das einen Artikel beschreibt und ein Feld für "verfügbare Menge" enthält, können Sie Probleme mit der Parallelität wie folgt behandeln:
- Rufen Sie das Dokument ab und notieren Sie sich die
_rev
Eigenschaft, die CouchDB mitsendet
- Dekrementieren Sie das Mengenfeld, wenn es größer als Null ist
- Senden Sie das aktualisierte Dokument mithilfe der
_rev
Eigenschaft zurück
- Wenn das
_rev
mit der aktuell gespeicherten Nummer übereinstimmt, fertig!
- Wenn ein Konflikt vorliegt (wenn er
_rev
nicht übereinstimmt), rufen Sie die neueste Dokumentversion ab
In diesem Fall müssen zwei mögliche Fehlerszenarien berücksichtigt werden. Wenn die neueste Dokumentversion eine Menge von 0 hat, behandeln Sie diese wie in einem RDBMS und warnen den Benutzer, dass er nicht kaufen kann, was er kaufen möchte. Wenn die neueste Dokumentversion eine Menge größer als 0 hat, wiederholen Sie einfach den Vorgang mit den aktualisierten Daten und beginnen wieder am Anfang. Dies zwingt Sie dazu, etwas mehr Arbeit zu leisten als ein RDBMS und kann bei häufigen, widersprüchlichen Updates etwas ärgerlich werden.
Die Antwort, die ich gerade gegeben habe, setzt voraus, dass Sie die Dinge in CouchDB genauso machen wie in einem RDBMS. Ich könnte dieses Problem etwas anders angehen:
Ich würde mit einem "Master-Produkt" -Dokument beginnen, das alle Deskriptordaten (Name, Bild, Beschreibung, Preis usw.) enthält. Dann würde ich für jede spezifische Instanz ein "Inventarticket" -Dokument mit Feldern für product_key
und hinzufügen claimed_by
. Wenn Sie ein Modell von Hammer verkaufen, und haben 20 von ihnen zu verkaufen, könnten Sie Dokumente mit Tasten wie hammer-1
, hammer-2
usw., um jeden verfügbaren Hammer darzustellen.
Dann würde ich eine Ansicht erstellen, die mir eine Liste der verfügbaren Hämmer mit einer Reduzierungsfunktion gibt, mit der ich eine "Summe" sehen kann. Diese sind völlig aus der Manschette, sollten Ihnen aber eine Vorstellung davon geben, wie eine Arbeitsansicht aussehen würde.
Karte
function(doc)
{
if (doc.type == 'inventory_ticket' && doc.claimed_by == null ) {
emit(doc.product_key, { 'inventory_ticket' :doc.id, '_rev' : doc._rev });
}
}
Dies gibt mir eine Liste der verfügbaren "Tickets" nach Produktschlüssel. Ich könnte mir eine Gruppe davon schnappen, wenn jemand einen Hammer kaufen möchte, und dann durch das Senden von Updates (mit id
und _rev
) iterieren, bis ich eines erfolgreich beanspruche (zuvor beanspruchte Tickets führen zu einem Update-Fehler).
Reduzieren
function (keys, values, combine) {
return values.length;
}
Diese Reduzierungsfunktion gibt einfach die Gesamtzahl der nicht beanspruchten inventory_ticket
Artikel zurück, sodass Sie feststellen können, wie viele "Hämmer" zum Kauf verfügbar sind.
Vorsichtsmaßnahmen
Diese Lösung entspricht ungefähr 3,5 Minuten Gesamtdenken für das von Ihnen vorgestellte Problem. Es gibt vielleicht bessere Möglichkeiten, dies zu tun! Das heißt, es reduziert widersprüchliche Updates erheblich und verringert die Notwendigkeit, auf einen Konflikt mit einem neuen Update zu reagieren. Bei diesem Modell werden nicht mehrere Benutzer versuchen, Daten im primären Produkteintrag zu ändern. Im schlimmsten Fall versuchen mehrere Benutzer, ein einzelnes Ticket zu beanspruchen. Wenn Sie mehrere davon aus Ihrer Sicht ausgewählt haben, fahren Sie einfach mit dem nächsten Ticket fort und versuchen es erneut.
Referenz: https://wiki.apache.org/couchdb/Frequently_asked_questions#How_do_I_use_transactions_with_CouchDB.3F
Erweiterung der Antwort von MrKurt. Für viele Szenarien müssen Sie keine Lagertickets einlösen. Anstatt das erste Ticket auszuwählen, können Sie zufällig aus den verbleibenden Tickets auswählen. Bei einer großen Anzahl von Tickets und einer großen Anzahl von gleichzeitigen Anfragen werden Sie weniger Konkurrenz zu diesen Tickets haben als alle anderen, die versuchen, das erste Ticket zu erhalten.
quelle
Ein Entwurfsmuster für erholsame Transaktionen besteht darin, eine "Spannung" im System zu erzeugen. Für den beliebten Anwendungsbeispiel einer Bankkontotransaktion müssen Sie sicherstellen, dass die Gesamtsumme für beide beteiligten Konten aktualisiert wird:
Das Scannen nach Spannung sollte in einem Backend-Prozess für alle "Spannungsdokumente" durchgeführt werden, um die Spannungszeiten im System kurz zu halten. Im obigen Beispiel wird eine kurze erwartete Inkonsistenz auftreten, wenn das erste Konto aktualisiert wurde, das zweite jedoch noch nicht aktualisiert wurde. Dies muss auf die gleiche Weise berücksichtigt werden, wie Sie mit eventueller Konsistenz umgehen, wenn Ihre Couchdb verteilt wird.
Eine andere mögliche Implementierung vermeidet die Notwendigkeit von Transaktionen vollständig: Speichern Sie einfach die Spannungsdokumente und bewerten Sie den Status Ihres Systems, indem Sie alle beteiligten Spannungsdokumente bewerten. Im obigen Beispiel würde dies bedeuten, dass die Gesamtsumme für ein Konto nur als die Summenwerte in den Transaktionsdokumenten bestimmt wird, an denen dieses Konto beteiligt ist. In Couchdb können Sie dies sehr gut als Karten- / Verkleinerungsansicht modellieren.
quelle
Nein, CouchDB ist im Allgemeinen nicht für Transaktionsanwendungen geeignet, da es keine atomaren Operationen in einer Cluster- / Replikationsumgebung unterstützt.
CouchDB opferte die Transaktionsfähigkeit zugunsten der Skalierbarkeit. Für atomare Operationen benötigen Sie ein zentrales Koordinationssystem, das Ihre Skalierbarkeit einschränkt.
Wenn Sie garantieren können, dass Sie nur eine CouchDB-Instanz haben oder dass jeder, der ein bestimmtes Dokument ändert, eine Verbindung zu derselben CouchDB-Instanz herstellt, können Sie das Konflikterkennungssystem verwenden, um mithilfe der oben beschriebenen Methoden eine Art Atomizität zu erstellen. Wenn Sie jedoch später auf einen Cluster skalieren Wenn Sie einen gehosteten Dienst wie Cloudant verwenden, wird dieser ausfallen und Sie müssen diesen Teil des Systems wiederholen.
Mein Vorschlag wäre also, etwas anderes als CouchDB für Ihren Kontostand zu verwenden. Auf diese Weise wird es viel einfacher.
quelle
Als Antwort auf das Problem des OP ist Couch hier wahrscheinlich nicht die beste Wahl. Die Verwendung von Ansichten ist eine gute Möglichkeit, den Überblick über das Inventar zu behalten, aber das Festklemmen auf 0 ist mehr oder weniger unmöglich. Das Problem ist die Rennbedingung, wenn Sie das Ergebnis einer Ansicht lesen, entscheiden, ob Sie in Ordnung sind, ein "Hammer-1" -Element zu verwenden, und dann ein Dokument schreiben, um es zu verwenden. Das Problem ist, dass es keine atomare Möglichkeit gibt, das Dokument nur zu schreiben, um den Hammer zu verwenden, wenn das Ergebnis der Ansicht ist, dass es> 0 Hammer-1 gibt. Wenn 100 Benutzer alle gleichzeitig die Ansicht abfragen und 1 Hammer-1 sehen, können sie alle ein Dokument schreiben, um einen Hammer 1 zu verwenden, was zu -99 Hammer-1 führt. In der Praxis ist die Rennbedingung ziemlich klein - sehr klein, wenn auf Ihrer Datenbank localhost ausgeführt wird. Sobald Sie jedoch skalieren und einen externen DB-Server oder Cluster haben, wird das Problem viel deutlicher.
Eine Aktualisierung der Antwort von MrKurt (möglicherweise nur datiert oder einige CouchDB-Funktionen sind ihm nicht bekannt)
Eine Ansicht ist ein guter Weg, um Dinge wie Salden / Vorräte in CouchDB zu handhaben.
Sie müssen die docid und rev in einer Ansicht nicht ausgeben. Sie erhalten beide kostenlos, wenn Sie die Ansichtsergebnisse abrufen. Wenn Sie sie senden - insbesondere in einem ausführlichen Format wie einem Wörterbuch - wird Ihre Ansicht nur unnötig groß.
Eine einfache Ansicht zum Verfolgen von Lagerbeständen sollte eher so aussehen (auch auf den ersten Blick).
Und die Reduktionsfunktion ist noch einfacher
Dies verwendet eine integrierte Reduktionsfunktion , die nur die Werte aller Zeilen mit übereinstimmenden Schlüsseln summiert.
In dieser Ansicht kann jedes Dokument ein Mitglied "InventoryChange" haben, das product_key's einer Änderung des Gesamtinventars zuordnet. dh.
Würde 10 hammer_1234 und 25 saw_4321 hinzufügen.
Würde 5 Hämmer aus dem Inventar verbrennen.
Mit diesem Modell aktualisieren Sie niemals Daten, sondern hängen sie nur an. Dies bedeutet, dass keine Möglichkeit für Aktualisierungskonflikte besteht. Alle Transaktionsprobleme beim Aktualisieren von Daten verschwinden :)
Eine weitere schöne Sache an diesem Modell ist, dass JEDES Dokument in der Datenbank Elemente zum Inventar hinzufügen und daraus entfernen kann. Diese Dokumente können alle Arten anderer Daten enthalten. Möglicherweise verfügen Sie über ein "Versand" -Dokument mit einer Reihe von Daten zu Empfangsdatum und -zeit, Lager, empfangendem Mitarbeiter usw. Solange dieses Dokument eine Inventaränderung definiert, wird das Inventar aktualisiert. Wie ein "Sale" -Dokument und ein "DamagedItem" -Dokument usw. Wenn sie jedes Dokument betrachten, lesen sie sich sehr deutlich. Und die Ansicht erledigt die ganze harte Arbeit.
quelle
Eigentlich kann man das in gewisser Weise. Schauen Sie sich die HTTP-Dokument-API an und scrollen Sie nach unten zur Überschrift "Mehrere Dokumente mit einer einzigen Anforderung ändern".
Grundsätzlich können Sie eine Reihe von Dokumenten in einer einzigen Post-Anfrage an URI / {Datenbankname} / _ Bulk_Docs erstellen / aktualisieren / löschen. Diese sind entweder alle erfolgreich oder alle schlagen fehl. Das Dokument weist jedoch darauf hin, dass sich dieses Verhalten in Zukunft ändern kann.
BEARBEITEN: Wie vorhergesagt, funktionieren die Massendokumente ab Version 0.9 nicht mehr auf diese Weise.
quelle
Verwenden Sie einfach eine einfache SQlite-Lösung für Transaktionen. Wenn die Transaktion erfolgreich abgeschlossen wurde, replizieren Sie sie und markieren Sie sie als repliziert in SQLite
SQLite-Tabelle
Sie können auch die Transaktionen löschen, die erfolgreich repliziert wurden.
quelle