Blog JSystems - uwalniamy wiedzę!

Szukaj

PostgreSQL

deadlock detected - jak zdiagnozować i uniknąć zakleszczeń

W skrócie

  • Aplikacja co jakiś czas dostaje błąd deadlock detected, a jedna z transakcji jest automatycznie wycofywana, mimo że nic z pozoru nie stoi.
  • Dwie (lub więcej) transakcje blokują zasoby w odwrotnej kolejności i czekają nawzajem na siebie. PostgreSQL wykrywa taki cykl i przerywa jedną z transakcji, żeby odblokować resztę.
  • Odczytaj z logu, które zapytania i wiersze utworzyły cykl, ujednolić kolejność dostępu do zasobów w kodzie i skróć transakcje, aby cykle w ogóle nie powstawały.

Deadlock to nie to samo co zwykła blokada. Przy zwykłej blokadzie jedna sesja czeka, aż druga skończy. Przy zakleszczeniu obie czekają na siebie i nigdy by się nie doczekały - dlatego PostgreSQL sam przerywa jedną z nich. Pokazujemy, jak odczytać przyczynę z logu i jak zaprojektować kod, żeby deadlocki zniknęły.

Jak to wygląda w praktyce

W logu aplikacji i serwera pojawia się ERROR: deadlock detected wraz z DETAIL, który opisuje, który proces czekał na który. Błąd ma kod SQLSTATE 40P01. Charakterystyczne jest to, że występuje nieregularnie, tylko przy zbieżności konkretnych operacji, i często pod obciążeniem. Jedna transakcja dostaje błąd i jest wycofywana, druga zwykle kończy się poprawnie.

Dlaczego tak się dzieje

Zakleszczenie powstaje, gdy transakcje pobierają blokady w niespójnej kolejności. Klasyczny przykład: transakcja A aktualizuje wiersz 1, potem chce wiersz 2. Transakcja B w tym samym czasie zaktualizowała wiersz 2, a teraz chce wiersz 1. A czeka na B, B czeka na A - cykl.

PostgreSQL nie zapobiega temu z góry, bo byłoby to kosztowne. Zamiast tego okresowo (co deadlock_timeout, domyślnie 1 sekunda) uruchamia detektor zakleszczeń. Gdy wykryje cykl, wybiera jedną z transakcji jako ofiarę i ją wycofuje, przerywając cykl. Ofiara dostaje błąd 40P01 i to jej zadaniem jest powtórzyć operację. Deadlocki są zawsze skutkiem wzorca dostępu w kodzie, a nie usterki bazy - dlatego rozwiązanie leży po stronie aplikacji.

Jak to rozwiązać krok po kroku

  1. Włącz pełne logowanie oczekiwania na blokady, żeby widzieć zaangażowane zapytania: ALTER SYSTEM SET log_lock_waits = on; i upewnij się, że log_min_messages obejmuje poziom ERROR. Wykonaj SELECT pg_reload_conf();.
  2. Odczytaj z logu sekcję DETAIL błędu deadlock detected. Znajdziesz tam PID-y procesów, konkretne zapytania i wiersze, które weszły w cykl. To pokazuje dokładnie, które dwie operacje się zakleszczyły.
  3. Ustal wspólną kolejność dostępu do zasobów. Jeśli operacje dotykają kilku wierszy albo tabel, zawsze blokuj je w tej samej kolejności (np. rosnąco po kluczu głównym). To najskuteczniejszy sposób - cykl nie powstanie, gdy wszyscy idą w tę samą stronę.
  4. Skracaj transakcje. Im krócej trzymasz blokady, tym mniejsze okno na cykl. Nie rób zapytań do zewnętrznych usług ani długich obliczeń w środku otwartej transakcji; pobierz dane, zapisz i szybko zrób COMMIT.
  5. Rozważ jawne, przewidywalne blokowanie na początku transakcji przez SELECT ... FOR UPDATE w ustalonej kolejności, zamiast pozwalać, by blokady powstawały przypadkowo w trakcie. Dla operacji na całej tabeli można użyć LOCK TABLE ... IN SHARE ROW EXCLUSIVE MODE.
  6. Dodaj w aplikacji automatyczne powtórzenie transakcji po błędzie 40P01. Ponieważ ofiara zakleszczenia jest zawsze wycofywana w całości, bezpieczne ponowienie całej transakcji zwykle kończy się sukcesem.

Jak sprawdzić, że zadziałało

Po ujednoliceniu kolejności dostępu obserwuj log serwera - komunikaty deadlock detected powinny przestać się pojawiać lub ich częstość drastycznie spaść. Możesz zliczyć wystąpienia w logu za ostatnią dobę, żeby porównać przed i po zmianie. Jeśli masz środowisko testowe, odtwórz scenariusz, który wcześniej wywoływał zakleszczenie (dwie równoległe transakcje w odwrotnej kolejności) i potwierdź, że teraz jedna czeka na drugą i obie kończą się poprawnie, zamiast wpaść w cykl.

Wróć do listy: 100 najczęstszych pytań i problemów z PostgreSQL

Szkolenie Administracja, replikacja i tuning baz danych PostgreSQL

Sprawdź szkolenie: Administracja, replikacja i tuning baz danych PostgreSQL

To szkolenie może być dofinansowane z KFS lub BUR.

★★★★★Średnia ocena naszych szkoleń w Google: 5/5

Szkolenie Zaawansowana administracja PostgreSQL - HA, DR, monitoring, skalowanie

Sprawdź szkolenie: Zaawansowana administracja PostgreSQL (HA, DR, monitoring, skalowanie)

To szkolenie może być dofinansowane z KFS lub BUR.

★★★★★Średnia ocena naszych szkoleń w Google: 5/5

Najczęściej zadawane pytania

Czym różni się deadlock od zwykłej blokady w PostgreSQL?
Przy zwykłej blokadzie jedna sesja czeka, aż druga zwolni zasób, i w końcu się doczeka. Przy deadlocku dwie transakcje czekają nawzajem na siebie i nigdy by się nie doczekały, więc baza przerywa jedną z nich.
Czy błąd deadlock detected oznacza usterkę bazy danych?
Nie, to skutek wzorca dostępu w kodzie aplikacji, a nie usterki PostgreSQL. Baza jedynie wykrywa cykl blokad i go rozrywa. Rozwiązanie polega na zmianie kolejności blokowania zasobów w aplikacji.
Jak najskuteczniej unikać zakleszczeń w PostgreSQL?
Zawsze pobieraj blokady na wielu wierszach lub tabelach w tej samej, ustalonej kolejności, na przykład rosnąco po kluczu głównym. Do tego skracaj transakcje, aby blokady były trzymane jak najkrócej.
Co powinna zrobić aplikacja po otrzymaniu błędu 40P01?
Powinna automatycznie powtórzyć całą wycofaną transakcję. Ofiara zakleszczenia jest zawsze wycofywana w całości, więc ponowne uruchomienie tej samej transakcji zwykle kończy się już sukcesem, bo cykl przestał istnieć.

Komentarze (0)

Musisz być zalogowany by móc dodać komentarz. Zaloguj się przez Google

Brak komentarzy...