Zakleszczenie (ang. deadlock) to sytuacja, w której dwie (lub więcej) transakcje wzajemnie blokują sobie drogę: każda trzyma blokadę na zasobie, którego potrzebuje druga, i żadna nie odda swojej, dopóki nie dostanie tej drugiej. Efekt? Patowy zastój — bez interwencji obie czekałyby w nieskończoność. Dlatego baza danych pełni rolę arbitra: wykrywa cykl i bezceremonialnie zabija jedną z transakcji (tzw. deadlock victim), żeby reszta mogła ruszyć dalej.
Jak to działa
Zakleszczenie wymaga blokad (locks), które bazy zakładają na wierszach, stronach czy tabelach, żeby zapewnić spójność przy współbieżnym dostępie. Problem pojawia się, gdy dwie transakcje sięgają po te same zasoby w różnej kolejności. Transakcja A blokuje wiersz 1 i chce wiersz 2; w tym samym czasie transakcja B blokuje wiersz 2 i chce wiersz 1. Powstaje cykl oczekiwania — i klasyczny deadlock.
Silnik bazy buduje w tle graf oczekiwań (wait-for graph) i okresowo szuka w nim cyklu. Gdy go znajdzie, wybiera ofiarę — zwykle tę transakcję, którą taniej wycofać (mniej zmian w logu) — i robi jej ROLLBACK. Aplikacja dostaje błąd i powinna po prostu spróbować ponownie.
Przykład z praktyki
W PostgreSQL dostaniesz wtedy ERROR: deadlock detected z kodem 40P01. W SQL Server to błąd 1205 z komunikatem w stylu „was deadlocked on lock resources… chosen as the deadlock victim”. Domyślnie Postgres sprawdza zakleszczenia po sekundzie oczekiwania (parametr deadlock_timeout). Diagnozę zaczniesz zwykle od logów albo, w MySQL/InnoDB, od komendy SHOW ENGINE INNODB STATUS, która pokazuje sekcję LATEST DETECTED DEADLOCK z dokładnym opisem obu transakcji.
Na co uważać
Najczęstszy mit: „deadlock to bug bazy danych”. Nie — to normalne zjawisko przy współbieżności i Twoim zadaniem jest go obsłużyć, a nie obrazić się. Druga pułapka to mylenie deadlocka z lock wait timeout (transakcja po prostu za długo czeka na blokadę — to nie cykl). Praktyczne rady:
- Sięgaj po zasoby w spójnej kolejności we wszystkich transakcjach — to eliminuje większość cykli.
- Trzymaj transakcje krótkie i nie rób w środku rzeczy typu odpytywanie API czy czekanie na użytkownika.
- Dodaj logikę retry na błąd deadlocka — z małym losowym opóźnieniem (backoff).
Pojęcia powiązane
Blokady (lock), transakcja, poziomy izolacji (isolation levels), MVCC, lock wait timeout, race condition oraz livelock — czyli kuzyn deadlocka, gdzie transakcje wprawdzie się ruszają, ale i tak nigdy nie kończą.