Direkt zum Hauptinhalt springen
eLearner.app
Modul 11 · Lektion 3 von 443/57 im Kurs~15 min
Lektionen des Moduls (3/4)

Locking und SELECT FOR UPDATE

Es ist einfach zu verstehen, wie eine Datenbank isoliert auf einem einzelnen Terminal funktioniert ... Nein, Moment, das habe ich bereits gesagt! Manchmal reichen „Atomic Updates“ (z. B. stock = stock - 1) nicht aus.

Stellen wir uns vor, Sie müssen prüfen, ob der Benutzer über genügend Guthaben verfügt, um Waren im Wert von 50 € zu kaufen:

  1. SELECT balance FROM users WHERE id = 1;
  2. JS: Wenn der Kontostand < 50 ist, blockieren Sie sie!
  3. Andernfalls fahren Sie mit dem Bezahlen fort...
  4. Führen Sie den Abzug durch! (Und hier finden Sie einen Benutzer, der bei der Validierung des Schecks in Schritt 2 einen weiteren parallelen Scheck ausgelöst hat, der sein Konto durchgebrannt hat, und jetzt sinken Sie unter Null!).

FOR UPDATE Explizite Sperre

Das Hinzufügen von FOR UPDATE am Ende eines SELECT verwandelt Ihren unschuldigen Lesevorgang auf magische Weise in eine versiegelte Sperre gegen andere Clients bis zum nächsten COMMIT. Niemand sonst kann UPDATE (oder gleichzeitige Lesevorgänge zur Aktualisierung) für diese bestimmten ausgewählten Zeilen durchführen, bis Sie entscheiden, was mit ihnen geschehen soll!

SQL
BEGIN;

-- If id=3 had already been locked by T1, T2 will sit in perpetual loading on this "read"
-- frozen in place, before receiving the raw data, until T1 runs COMMIT freeing everyone!
SELECT balance
FROM users
WHERE id = 3
FOR UPDATE;

-- at this point our Node JS instance lives with the guarantee, knowing for certain that NO ONE ELSE in the world has
-- manipulated (nor been able to read with intent to alter) that balance while we complete the application logic

UPDATE users SET balance = balance - 50 WHERE id = 3;

COMMIT;

Es gibt sogar FOR SHARE, der es anderen Lesern ermöglicht, weiterzulesen (bei einer Basissperre), ohne Statuslesevorgänge zu blockieren, aber jede mögliche nachfolgende Aktualisierung blockiert, bis Sie einen Commit durchführen. Für Änderungen nutzen Sie jedoch immer die rohe Gewalt der exklusiven Sperrklausel:

Probieren Sie es selbst aus

Übung#sql.m11.l3.e1
Versuche: 0Wird geladen…

Öffnen Sie eine normale zeitbezogene Transaktion und sperren Sie ausschließlich das Lesen der Produkt-ID=5. Führen Sie eine zweistufige Abfrage aus: 1. Öffnen Sie die Transaktion (BEGIN) 2. Fragen Sie „Produkte“ ab und rufen Sie die vollständige Zeile für ID 5 ab, mit der Garantie, dass niemand sie ändern wird, indem Sie die Option „FOR UPDATE“ anhängen.

Editor wird geladen…
Hinweis anzeigen

BEGIN;, newline, SELECT * FROM products WHERE id = 5 FOR UPDATE;

Lösung nach 3 Versuchen verfügbar

Übung#sql.m11.l3.e2
Versuche: 0Wird geladen…

Manchmal hängt die Transaktion für katastrophale Zeiträume (Deadlock oder unendliche Wartezeiten). Um dies zu verhindern, können Sie der Datenbank befehlen, den Vorgang aufzugeben, sobald die Zeile nicht sofort verfügbar ist: Verwenden Sie NOWAIT am Ende, was dazu führen würde, dass eine SQL Server-Ausnahme an JS zurückspringt, um sie abzufangen, ohne für immer zu hängen. Führen Sie ein SELECT für Kategorie-ID=1 aus und verwenden Sie FOR UPDATE, erzwingen Sie jedoch NOWAIT.

Editor wird geladen…
Hinweis anzeigen

SELECT * FROM groups WHERE id = 1 FOR UPDATE NOWAIT;

Lösung nach 3 Versuchen verfügbar