Transakcje w PostgreSQL zapewniają izolację — jedną z kluczowych cech ACID. Do tego system używa blokad. Blokada to mechanizm, który gwarantuje, że kilka transakcji nie będzie się "bić" o ten sam rekord albo tabelę. Wyobraź sobie kolejkę w sklepie: przy kasie obsługiwany jest jeden człowiek na raz. Blokady działają podobnie, tylko pilnują dostępu do danych w tabelach.
Podstawowe typy blokad
W PostgreSQL jest kilka rodzajów blokad, niektóre z nich mogłeś już widzieć w EXPLAIN ANALYZE:
ROW EXCLUSIVE(ekskluzywna blokada wiersza) — pojawia się przy zmianie danych w wierszu. To najczęściej używany typ blokady, np. przyINSERT,UPDATEalboDELETE.SHARE(współdzielona blokada) — używana przy operacjach, które gwarantują, że dane się nie zmienią, póki trwa zapytanie, np. przySELECT ... FOR SHARE.EXCLUSIVE(ekskluzywna blokada) — blokuje wszystkie inne operacje na danych poza odczytem.
Można powiedzieć, że blokady to takie psy stróżujące, które pilnują naszych danych.
Problem blokad: co może pójść nie tak?
Teraz, gdy już wiemy, czym są blokady, zobaczmy, jakie mogą sprawiać problemy. Największy z nich to problem deadlocków. Deadlock (wzajemna blokada) to sytuacja, w której dwie (albo więcej) transakcje czekają na siebie nawzajem, przez co wykonanie utknęło w nieskończonej pętli. Typowo wygląda to tak:
- Transakcja 1 blokuje wiersz A i chce dostać się do wiersza B.
- Transakcja 2 blokuje wiersz B i chce dostać się do wiersza A.
- Ponieważ oba wiersze są zablokowane, żadna z transakcji nie może się zakończyć.
Przykład deadlocka
Żeby poczuć ból programisty, który trafił na deadlocka, wyobraź sobie taki scenariusz:
-- Transakcja 1
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- Jednocześnie
-- Transakcja 2
BEGIN;
UPDATE accounts SET balance = balance + 200 WHERE id = 2;
-- Transakcja 1 próbuje zablokować id = 2
UPDATE accounts SET balance = balance - 100 WHERE id = 2;
-- Transakcja 2 próbuje zablokować id = 1
UPDATE accounts SET balance = balance + 200 WHERE id = 1;
Voila! Każda transakcja jest zablokowana przez drugą. PostgreSQL prędzej czy później to wykryje i rzuci błąd deadlocka.
Jak unikać deadlocków?
- Stała kolejność dostępu do danych. Staraj się zawsze odwoływać do danych w tej samej kolejności. Jeśli transakcja 1 najpierw blokuje wiersz A, potem wiersz B, to transakcja 2 powinna robić to samo: najpierw wiersz A, potem wiersz B.
-- Prawidłowa kolejność dostępu
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 200 WHERE id = 2;
COMMIT;
Minimalizuj czas trwania transakcji. Długie transakcje zwiększają szansę na deadlocka. Kończ transakcje jak najszybciej:
BEGIN, zrób wszystkie potrzebne zmiany i od razuCOMMIT.Używaj poziomów izolacji transakcji. Jeśli Twoje zadanie nie wymaga super ścisłej izolacji danych, rozważ użycie poziomu izolacji
READ COMMITTED. Pozwala to uniknąć niektórych blokad.
Timeouty i diagnostyka blokad
Są narzędzia do diagnozowania i zapobiegania blokadom w PostgreSQL. Na przykład możesz ustawić maksymalny czas oczekiwania na blokadę:
SET lock_timeout = '5s'; -- Ustawiamy timeout blokady na 5 sekund
Jeśli blokada trwa dłużej niż podany czas, transakcja zostanie przerwana, co zapobiega totalnemu zacięciu.
Śledzenie blokad w PostgreSQL
Jedno z przydatnych poleceń do monitorowania blokad to pg_locks. Pokazuje wszystkie aktywne blokady w systemie:
SELECT * FROM pg_locks;
Możesz zobaczyć, które transakcje trzymają blokady, a które czekają na ich zwolnienie. Szczególnie przydatne przy debugowaniu deadlocków.
Specyfika LOCK i ręczne blokowanie obiektów
Jeśli musisz ręcznie zarządzać blokadami, użyj polecenia LOCK:
LOCK TABLE orders IN ACCESS EXCLUSIVE MODE;
Uwaga: ACCESS EXCLUSIVE to najmocniejsza blokada, blokuje wszystkie inne operacje na tabeli, nawet SELECT. Używaj jej tylko w wyjątkowych przypadkach (np. zmiana struktury tabeli).
Do blokowania pojedynczych wierszy przy zmianach użyj SELECT ... FOR UPDATE:
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
To gwarantuje, że inne transakcje nie zmienią tych wierszy do końca Twojej transakcji.
Co warto pamiętać o blokadach?
Blokady to nie zło, tylko narzędzie. Pomagają dbać o integralność danych, ale trzeba z nimi uważać. Oto kilka kluczowych porad:
- Nie zaczynaj transakcji, jeśli nie wiesz dokładnie, po co Ci ona.
- Kończ transakcję jak najszybciej.
- Unikaj dostępu do danych w różnej kolejności.
- Używaj poziomów izolacji odpowiednich do Twojego zadania.
Teraz jesteś gotowy na pracę z blokadami i deadlockami! Niech pg_locks i mądrość ACID będą z Tobą.
GO TO FULL VERSION