Wyobraź sobie, że pracujesz w kawiarni, gdzie jeden kelner sprawdza zapas tortów na kuchni, a drugi przyjmuje zamówienie na tort od nowego klienta. W idealnym świecie obaj powinni pracować na tych samych danych o liczbie tortów, żeby uniknąć błędów typu "podwójna rezerwacja". Ale w realnym świecie mogą pojawić się problemy z równoległym wykonywaniem operacji.
Oto trzy główne nieprzyjemności, które mogą się wydarzyć:
Dirty Read (brudny odczyt): Jeden zapytanie widzi zmiany wprowadzone przez inne, ale jeszcze nie zatwierdzone. Jeśli te zmiany zostaną wycofane, pierwsze zapytanie okaże się naiwne jak student na pierwszej rozmowie o pracę.
Non-Repeatable Read (niepowtarzalny odczyt): Jedno zapytanie dwa razy czyta te same dane, ale między tymi odczytami ktoś inny zdąży je zmienić. To jak przyjść na dworzec, zobaczyć rozkład pociągów, wrócić za minutę — i odkryć, że pociąg został odwołany. Albo twój bilet został kupiony, gdy szukałeś kasy :)
Phantom Read (fantomowy odczyt): Jedno zapytanie widzi podzbiór wierszy, ale między dwoma wykonaniami ktoś dodaje nowe wiersze, które wpływają na wynik. To jakby twoja firma przegrała przetarg, a potem wszystkie zgłoszenia oprócz twojego i zgłoszenia żony burmistrza zostały anulowane.
Poziomy izolacji transakcji
Teraz, gdy już znamy problemy, czas zajrzeć do narzędzia, które PostgreSQL daje do ich rozwiązania — poziomy izolacji transakcji. To jak ustalanie zasad współpracy między równoległymi transakcjami. Im wyższy poziom izolacji, tym większa gwarancja, że transakcje nie będą sobie przeszkadzać. Ale za to płacisz niższą "szybkością obsługi", czyli wydajnością.
Poziomy izolacji w PostgreSQL
Read Uncommitted (Odczyt niezatwierdzonych danych):
- Pozwala na odczyt zmian, które jeszcze nie zostały zatwierdzone (tak, to brudny odczyt w całej okazałości).
- W PostgreSQL ten poziom jest w rzeczywistości realizowany jako
Read Committed, więc w czystej postaci nie jest obsługiwany. PostgreSQL odmawia jego implementacji, bo to podejście jest zbyt niepewne.
Read Committed (Odczyt zatwierdzonych zmian):
- Zapobiega brudnemu odczytowi.
- Transakcja widzi tylko te dane, które zostały zatwierdzone w momencie wykonania jej polecenia.
- Jednak możliwe są
Non-Repeatable ReadiPhantom Read.
Repeatable Read (Powtarzalny odczyt):
- Gwarantuje, że dane, które czytasz, pozostaną niezmienione podczas trwania transakcji.
- Zapobiega brudnemu odczytowi i niepowtarzalnemu odczytowi.
- Jednak fantomowe wiersze nadal są możliwe.
Serializable (Serializowalny):
- Gwarantuje, że transakcje są wykonywane tak, jakby były wykonywane po kolei, jedna po drugiej.
- Zapobiega wszystkim trzem problemom: brudnemu odczytowi, niepowtarzalnemu odczytowi i fantomowym wierszom.
- Najbardziej rygorystyczny — i najwolniejszy — poziom izolacji.
Dlaczego izolacja transakcji jest ważna?
Wyobraź sobie teraz bazę danych sklepu internetowego, gdzie tysiąc użytkowników próbuje jednocześnie złożyć zamówienie. Bez dobrze ustawionego poziomu izolacji możesz mieć mnóstwo konfliktów: od "znikających" towarów po zdublowane zamówienia.
Wybór odpowiedniego poziomu izolacji pomaga radzić sobie z konkurencyjnym wykonywaniem transakcji, zapewniając balans między wydajnością a integralnością danych. Na przykład:
- W systemach analitycznych częściej wybiera się minimalne poziomy izolacji (np. Read Committed), bo dokładność danych nie zawsze jest krytyczna.
- W systemach finansowych preferowany jest poziom Serializable, żeby uniknąć błędów w obliczeniach lub podwójnych operacji.
Przykłady użycia poziomów izolacji
- Read Committed
Ten poziom zapewnia, że nigdy nie odczytasz danych, które mogłyby zostać wycofane.
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
-- Odczytaj dane konta.
SELECT balance FROM accounts WHERE account_id = 1;
-- Jeśli inna transakcja zaktualizuje saldo, te dane natychmiast się zaktualizują.
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
- Repeatable Read
Ten poziom gwarantuje, że jeśli czytasz dane, pozostaną one niezmienne dla ciebie przez całą transakcję.
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
-- Odczytaj dane konta.
SELECT balance FROM accounts WHERE account_id = 1;
-- Nawet jeśli inna transakcja zaktualizuje to saldo, nie zobaczysz zmian.
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
- Serializable
Na tym poziomie transakcja działa tak, jakby była jedyną w systemie.
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
-- Odczytaj dane konta.
SELECT balance FROM accounts WHERE account_id = 1;
-- Każda inna transakcja próbująca zmienić te dane zostanie zablokowana do zakończenia twojej transakcji.
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
Jak wybrać poziom izolacji?
Wybór poziomu izolacji zależy od twoich wymagań:
- Jeśli szybkość jest ważniejsza niż dokładność i możesz zaakceptować fantomowe wiersze — wybierz
Read Committed. - Jeśli dokładność jest ważna, ale nadal chcesz zachować wysoką wydajność — użyj
Repeatable Read. - Jeśli potrzebujesz 100% gwarancji poprawności danych, nawet kosztem szybkości — wybierz
Serializable.
Uważaj! Rygorystyczne poziomy izolacji mogą prowadzić do blokad i spadku wydajności systemu. Rozsądne rozwiązanie to kompromis między wydajnością a spójnością danych.
GO TO FULL VERSION