Przejdź do głównej treści
eLearner.app
Moduł 11 · Lekcja 2 z 442/57 w kursie~10 min
Lekcje modułu (2/4)

Wyścigi (race conditions)

Zrozumienie, jak baza danych działa w izolacji na jednym terminalu, jest proste, ale w prawdziwym scenariuszu sieciowym będą setki współbieżnych procesów „Node/PHP/Go” wysyłających żądania. To tutaj rodzą się słynne warunki wyścigowe.

Problem „Utracona aktualizacja”.

Utracona aktualizacja ma miejsce, gdy dwa wątki czytają ten sam stary wiersz, wykonaj kilka obliczeń na serwerze JavaScript, a następnie odeślij UPDATE, aby zapisać wynik.

Scenariusz:

  • Produkt #5 ma 10 jednostek.
  • Użytkownik A kupuje jeden (serwer A odczytuje „10”, oblicza „9”).
  • W tej samej milisekundie użytkownik B kupuje kolejny, zanim zakup A zakończy fazę COMMIT.
  • Serwer B również odczytuje „10”, oblicza „9”.
  • Serwer A wysyła UPDATE: set quantity = 9.
  • Serwer B wysyła UPDATE: set quantity = 9.
  • Obaj uważają, że pomyślnie zmniejszyli liczbę. Ale ostateczna wartość powinna wynosić 8 jednostek! Zakup fantomowy został utracony.

Rozwiązanie bez dużych transakcji (aktualizacje atomowe)

Zamiast czytać i wykonywać obliczenia na zewnątrz, pozwól DB zrobić to podczas samej aktualizacji, która jest zawsze synchronizowana.

SQL
-- Wrong
UPDATE products SET stock = 9 WHERE id = 5;

-- Correct and natively thread-safe
UPDATE products SET stock = stock - 1 WHERE id = 5;

Bardziej ekstremalne anomalie (brudne odczyty i fantomy)

  • Dirty Read: Czytasz tymczasowe dane innej osoby, które nie zostały jeszcze potwierdzone przez COMMIT (i mogą nagle przejść do funkcji ROLLBACK, unieważniając Cię).
  • Odczyt widmowy: skanujesz wszystkie wiersze, w międzyczasie drugi wątek wstawia nowy rekord, następnie wykonujesz kolejne skanowanie podczas tej samej transakcji i w magiczny sposób odkrywasz, że nowy rekord pojawił się nie wiadomo skąd, narażając obliczone raporty finansowe.
Ćwiczenie#sql.m11.l2.e1
Próby: 0Ładowanie...

Atomicznie zmodyfikuj rekord klientów w celu manipulacji małymi ciągami znaków. Powiedzmy, że chcemy zmienić nazwę „miasta” klienta, aby dodać gwiazdkę. Zrób to, wykorzystując własną wartość wiersza dla tej o id=10, łącząc „ - Old” z bieżącą wartością (Użyj potoku łączącego || postgres).

Ładowanie edytora...
Pokaż wskazówkę

AKTUALIZUJ klientów SET miasto = miasto || ' - Stary' GDZIE id = 10;

Rozwiązanie dostępne po 3 próbach

Ćwiczenie#sql.m11.l2.e2
Próby: 0Ładowanie...

Klasyczna utracona aktualizacja ma miejsce w przypadku płatności i cen. Załóżmy, że następuje awaryjna podwyżka cen: zwiększ wartość „cena_jednostkowa” tabeli „order_items” (dla produktu 1 z zamówienia 1) o 20% atomowo, tj. mnożąc ją * 1,2 zamiast wstawiać nową cenę obliczoną ręcznie w JS.

Ładowanie edytora...
Pokaż wskazówkę

AKTUALIZUJ elementy_zamówienia SET cena_jednostkowa = cena_jednostkowa * 1.2 GDZIE id_zamówienia = 1 ORAZ id_produktu = 1;

Rozwiązanie dostępne po 3 próbach