Wie implementiert man optimistisches Sperren in MySQL richtig?
Unser Team hat festgestellt, dass wir # 4 unten ausführen müssen, da sonst das Risiko besteht, dass ein anderer Thread dieselbe Version des Datensatzes aktualisieren kann. Wir möchten jedoch bestätigen, dass dies der beste Weg ist, dies zu tun.
- Erstellen Sie ein Versionsfeld in der Tabelle, für die Sie eine optimistische Sperre verwenden möchten, z. B. Spaltenname = "Version".
- Stellen Sie bei der Auswahl sicher, dass Sie die Versionsspalte einschließen und die Version notieren
- Bei einer nachfolgenden Aktualisierung des Datensatzes sollte die Aktualisierungsanweisung "where version = X" ausgeben, wobei X die Version ist, die wir in # 2 erhalten haben, und das Versionsfeld während dieser Aktualisierungsanweisung auf X + 1 setzen
- Führen Sie eine
SELECT FOR UPDATE
Aktualisierung des Datensatzes durch, den wir aktualisieren möchten, damit wir serialisieren, wer Änderungen an dem Datensatz vornehmen kann, den wir aktualisieren möchten.
Zur Verdeutlichung versuchen wir zu verhindern, dass zwei Threads, die denselben Datensatz im selben Zeitfenster auswählen, in dem sie dieselbe Version des Datensatzes abrufen, sich gegenseitig überschreiben, wenn sie versuchen, den Datensatz gleichzeitig zu aktualisieren. Wir glauben, dass, wenn wir nicht # 4 tun, die Möglichkeit besteht, dass, wenn beide Threads ihre jeweiligen Transaktionen gleichzeitig eingeben (aber ihre Updates noch nicht veröffentlicht haben), wenn sie zur Aktualisierung gehen, der zweite Thread, der das UPDATE verwendet ... wobei version = X mit alten Daten arbeitet.
Denken wir zu Recht, dass wir diese pessimistische Sperrung beim Aktualisieren durchführen müssen, obwohl wir Versionsfelder / optimistische Sperrung verwenden?
SELECT ... FOR UPDATE
oder eine optimistische Sperre durch Zeilenversionierung, nicht beide. Siehe Detail in der Antwort.Antworten:
Ihr Entwickler irrt sich. Sie benötigen eine
SELECT ... FOR UPDATE
oder eine Zeilenversionierung, nicht beide.Probieren Sie es aus und sehen Sie. Öffnen drei MySQL - Sitzungen
(A)
,(B)
und(C)
auf die gleiche Datenbank.In
(C)
Ausgabe:In beiden
(A)
und in(B)
einemUPDATE
, der die Zeilenversion testet und festlegt, ändern Sie denwinner
Text in jeder, damit Sie sehen können, welche Sitzung welche ist:Jetzt in
(C)
,UNLOCK TABLES;
um das Schloss zu lösen.(A)
und(B)
wird um die Zeilensperre rennen. Einer von ihnen wird gewinnen und das Schloss bekommen. Der andere blockiert das Schloss. Der Gewinner, der die Sperre erhalten hat, wechselt die Zeile. Angenommen,(A)
der Gewinner ist, können Sie jetzt die geänderte Zeile (immer noch nicht festgeschrieben, also für andere Transaktionen nicht sichtbar) mit a sehenSELECT * FROM test WHERE id = 1
.COMMIT
Sagen wir jetzt in der Gewinnersitzung(A)
.(B)
wird die Sperre erhalten und mit dem Update fortfahren. Die Version stimmt jedoch nicht mehr überein, sodass keine Zeilen geändert werden, wie aus dem Ergebnis der Zeilenanzahl hervorgeht. Nur einerUPDATE
hatte einen Effekt, und die Client-Anwendung kann klar erkennen, welcherUPDATE
erfolgreich war und welcher fehlgeschlagen ist. Es ist keine weitere Verriegelung erforderlich.Siehe Sitzungsprotokolle bei Pastebin hier . Ich habe
mysql --prompt="A> "
usw. verwendet, um es einfach zu machen, den Unterschied zwischen Sitzungen zu erkennen. Ich habe die Ausgabe in zeitlicher Reihenfolge kopiert und eingefügt, daher handelt es sich nicht um eine vollständige Rohausgabe, und es ist möglich, dass ich beim Kopieren und Einfügen Fehler gemacht habe. Testen Sie es selbst, um zu sehen.Wenn Sie hatte nicht eine Zeile Versionsfeld hinzugefügt, dann müssten Sie
SELECT ... FOR UPDATE
der Lage sein, zuverlässig Ordnung zu gewährleisten.Wenn Sie darüber nachdenken,
SELECT ... FOR UPDATE
ist a völlig redundant, wenn Sie sofort eineUPDATE
Daten ohne Wiederverwendung von Daten aus demSELECT
oder ohne Zeilenversionierung ausführen. DerUPDATE
wird sowieso eine Sperre nehmen. Wenn jemand anderes die Zeile zwischen Ihrem Lesen und dem anschließenden Schreiben aktualisiert, stimmt Ihre Version nicht mehr überein, sodass Ihr Update fehlschlägt. So funktioniert optimistisches Sperren.Der Zweck von
SELECT ... FOR UPDATE
ist:SERIALIZABLE
Isolation oder Zeilenversionierung verwenden zu müssen.Sie müssen nicht sowohl optimistisches Sperren (Zeilenversionierung) als auch verwenden
SELECT ... FOR UPDATE
. Verwenden Sie den einen oder anderen.quelle
Keine Sperren (keine Tabelle, keine Transaktion) erforderlich oder sogar erwünscht:
quelle