Lektionen des Moduls (2/4)
Race Conditions
Es ist einfach zu verstehen, wie eine Datenbank isoliert auf einem einzelnen Terminal funktioniert, aber in einem echten Webszenario gibt es Hunderte von gleichzeitigen „Node/PHP/Go“-Prozessen, die Anfragen senden. Hier entstehen die berühmten Race Conditions.
Das Problem „Verlorenes Update“.
Ein verlorenes Update tritt auf, wenn zwei Threads dieselbe alte Zeile lesen, auf dem JavaScript-Server Berechnungen durchführen und dann das UPDATE zurücksenden, um das Ergebnis zu speichern.
Szenario:
- Produkt #5 hat 10 Einheiten.
- Benutzer A kauft eins (Server A liest „10“, berechnet „9“).
- In derselben Millisekunde kauft Benutzer B einen weiteren, bevor der Kauf von A die COMMIT-Phase abschließt.
- Server B liest ebenfalls „10“ und berechnet „9“.
- Server A sendet
UPDATE: set quantity = 9. - Server B sendet
UPDATE: set quantity = 9. - Beide glauben, dass sie die Zahl erfolgreich verringert haben. Der Endwert hätte aber 8 Einheiten betragen sollen! Ein Phantomkauf ist verloren gegangen.
Lösung ohne schwere Transaktionen (Atomic Updates)
Anstatt draußen zu lesen und zu rechnen, überlassen Sie dies der Datenbank während des Updates selbst, das immer synchronisiert ist.
-- Wrong
UPDATE products SET stock = 9 WHERE id = 5;
-- Correct and natively thread-safe
UPDATE products SET stock = stock - 1 WHERE id = 5;Weitere extreme Anomalien (Dirty Reads & Phantoms)
- Dirty Read: Sie haben die temporären Daten einer anderen Person gelesen, die noch nicht durch COMMIT bestätigt wurde (und plötzlich zu einem ROLLBACK übergehen könnte, das Sie ungültig macht).
- Phantom-Lesevorgänge: Sie führen einen Scan aller Zeilen durch, während ein zweiter Thread einen neuen Datensatz einfügt, dann führen Sie einen weiteren Scan während derselben Transaktion durch und stellen auf magische Weise fest, dass der neue Datensatz aus dem Nichts aufgetaucht ist, was die berechneten Finanzberichte gefährdet.
Ändern Sie den Kundendatensatz atomar für eine kleine Zeichenfolgenmanipulation. Angenommen, wir möchten die „Stadt“ eines Kunden umbenennen, um ein Sternchen hinzuzufügen. Nutzen Sie dazu den eigenen Wert der Zeile für den mit der ID=10 und verketten Sie „- Old“ mit dem aktuellen Wert (verwenden Sie die Postgres-Verkettungspipe ||).
Hinweis anzeigen
Kunden aktualisieren SET Stadt = Stadt || ' - Alt' WHERE id = 10;
Lösung nach 3 Versuchen verfügbar
Eine klassische verlorene Aktualisierung passiert bei Zahlungen und Preisen. Nehmen wir an, es gibt eine Notpreiserhöhung: Erhöhen Sie den „unit_price“ der Tabelle „order_items“ (für Produkt 1 von Bestellung 1) um atomare 20 %, d. h. multiplizieren Sie ihn mit 1,2, anstatt den neuen, manuell in JS vorberechneten Preis einzufügen.
Hinweis anzeigen
UPDATE order_items SET unit_price = unit_price * 1.2 WHERE order_id = 1 AND product_id = 1;
Lösung nach 3 Versuchen verfügbar