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

Blokowanie i SELECT FOR UPDATE

Zrozumienie, jak baza danych działa w izolacji na jednym terminalu, jest proste… nie, czekaj, już to mówiłem! Czasami „Aktualizacje atomowe” (np. stock = stock - 1) nie wystarczą.

Wyobraźmy sobie, że musisz sprawdzić, czy użytkownik ma wystarczające saldo, aby kupić rzeczy o wartości 50 €:

  1. KODEF0
  2. JS: Jeśli saldo wynosi < 50, zablokuj ich!
  3. W przeciwnym razie przejdź do kasy...
  4. Uruchom odliczenie! (A tutaj znajdziesz użytkownika, który podczas sprawdzania czeku w kroku 2 wystrzelił inny równoległy, który spalił jego konto i teraz spadasz poniżej zera!).

DLA AKTUALIZACJI Wyraźne blokowanie

Dodanie FOR UPDATE na końcu SELECT magicznie przekształca twój niewinny odczyt w zapieczętowaną blokadę przeciwko innym klientom aż do następnego COMMIT. Nikt inny nie będzie mógł wykonać AKTUALIZACJI (lub równoczesnego odczytu w celu aktualizacji) w tych konkretnych wybranych wierszach, dopóki nie zdecydujesz, co z nimi zrobić!

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;

Istnieje nawet FOR SHARE, który pozwala innym czytelnikom kontynuować czytanie (jeśli są na blokadzie podstawowej) bez blokowania odczytów stanu, ale blokowania wszelkich możliwych kolejnych aktualizacji, dopóki nie zatwierdzisz. Jednak w przypadku modyfikacji zawsze używaj brutalnej siły wyłącznej klauzuli blokującej:

Spróbuj sam

Ćwiczenie#sql.m11.l3.e1
Próby: 0Ładowanie...

Otwórz normalną transakcję ograniczoną w czasie i zablokuj wyłącznie odczyt identyfikatora produktu=5. Uruchom zapytanie dwuetapowe: 1. Otwórz transakcję (BEGIN) 2. Zapytanie „produkty” pobierze pełny wiersz dla identyfikatora 5 z gwarancją, że nikt go nie zmieni, poprzez dołączenie opcji „DO AKTUALIZACJI”.

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

BEGIN;, nowa linia, WYBIERZ * Z produktów GDZIE id = 5 DLA AKTUALIZACJI;

Rozwiązanie dostępne po 3 próbach

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

Czasami transakcja zawiesza się na katastrofalne okresy (zakleszczenie lub nieskończone oczekiwanie). Aby temu zapobiec, możesz nakazać bazie danych, aby zrezygnowała w momencie, gdy wiersz nie jest od razu dostępny: używając na końcu NOWAIT, co spowodowałoby powrót wyjątku serwera SQL do JS w celu jego przechwycenia bez zawieszania się na zawsze. Uruchom polecenie SELECT dla kategorii ID=1 i użyj funkcji FOR UPDATE, ale wymuszając NOWAIT.

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

WYBIERZ * Z kategorii GDZIE id = 1 DLA AKTUALIZACJI NOWIT;

Rozwiązanie dostępne po 3 próbach