database SELECT FOR UPDATE SKIP LOCKED - ghdrako/doc_snipets GitHub Wiki
Klauzula ta jest używana w bazach danych (np. PostgreSQL, MySQL, Oracle) podczas operacji współbieżnych. Standardowe zapytanie SELECT ... FOR UPDATE blokuje wiersze do momentu zakończenia transakcji, co oznacza, że inne procesy muszą czekać w kolejce. Dodanie modyfikatora SKIP LOCKED sprawia, że baza danych pomija wiersze, które są obecnie zablokowane przez inne transakcje, i zwraca tylko te, które są natychmiast dostępne.
W systemie kolejkowym działa to w następujący sposób:
- Brak przestojów (non-blocking): Gdy worker próbuje pobrać zadanie za pomocą SELECT ... FOR UPDATE SKIP LOCKED, baza danych sprawdza, które wiersze są obecnie przetwarzane przez inne workery.
- Pominięcie zajętych: Zamiast czekać (wstrzymywać wątek), aż inne transakcje zwolnią wiersze, baza po prostu je ignoruje i zwraca pierwsze wolne zadanie, które nie jest w tej chwili zablokowane.
- Bezpieczeństwo współbieżności: Ponieważ zapytanie używa FOR UPDATE, ten jeden, wybrany wiersz zostaje natychmiast zablokowany dla innych, dzięki czemu żaden inny worker nie pobierze tego samego zadania.
Przyklad Triki, które sprawiły, że to działa (poza SKIP LOCKED)
Shopify nie użyło tylko samego polecenia. Zrobili kilka bardzo sprytnych rzeczy, żeby to "latało":
- Podejście "jeden wiersz na jednostkę towaru": Zamiast trzymać wiersz "Item A: 100 sztuk", stworzyli tabelę, w której każda sztuka towaru to osobny wiersz. Dzięki temu 100 osób może kupować ten sam towar w tym samym czasie, a SKIP LOCKED sprawia, że każdy "bierze" inny wiersz (inną sztukę) bez czekania na innych.
- Optymalizacja blokad (Composite Primary Keys): Odkryli, że domyślne użycie auto-inkrementowanych ID powoduje niepotrzebne blokady (locking) indeksów. Zmienili to na klucze złożone, co zredukowało liczbę blokad do niezbędnego minimum (jeden wiersz = jedna blokada).
- Zmiana poziomu izolacji (READ COMMITTED): Domyślny poziom izolacji w MySQL (REPEATABLE READ) powoduje tzw. gap locks (blokowanie zakresów). Przy wysokim obciążeniu blokowało to możliwość dodawania nowych wierszy (uzupełniania zapasów). Przejście na READ COMMITTED pozwoliło na płynne działanie.
- Utrzymywanie "puli" wierszy: Aby tabela nie rosła do milionów rekordów, utrzymują tylko ograniczoną pulę (np. 1000 dostępnych jednostek na towar). Jeśli pula się kończy, system automatycznie ją uzupełnia.