programowanieBackend developer

Opowiedz szczegółowo o specyfice pracy z transakcjami w SQL. Jak kontrolować integralność danych przy jednoczesnym dostępie do tej samej tabeli z różnych sesji?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

W SQL transakcje pozwalają grupować kilka operacji (insert/update/delete) w jedną atomową jednostkę pracy, którą można albo całkowicie zastosować, albo cofnąć. Cykl życia transakcji opiera się na komendach:

  • BEGIN lub START TRANSACTION — początek transakcji;
  • COMMIT — zatwierdzenie zmian;
  • ROLLBACK — cofnięcie wszystkich zmian wewnątrz transakcji.

SQL wspiera poziomy izolacji transakcji (Read Uncommitted, Read Committed, Repeatable Read, Serializable), które definiują widoczność danych pomiędzy równoległymi transakcjami i chronią przed problemami takimi jak "brudne odczyty" czy "fantomowe wiersze".

Aby kontrolować integralność danych, potrzebne są odpowiednie:

  • Wybór poziomu izolacji (na przykład, dla aplikacji bankowych — najprawdopodobniej Serializable).
  • Jawne zarządzanie transakcjami, szczególnie tam, gdzie jedna encja jest edytowana jednocześnie (na przykład, SELECT ... FOR UPDATE).

Przykład na PostgreSQL:

BEGIN; -- Otrzymujemy i blokujemy wiersz produktu SELECT * FROM inventory WHERE id = 1 FOR UPDATE; UPDATE inventory SET quantity = quantity - 1 WHERE id = 1; COMMIT;

Pytanie z podchwytliwym pytaniem

Jaki poziom izolacji jest domyślnie ustawiony w popularnych SGBD (PostgreSQL, MySQL) i czym różni się od SERIALIZABLE?

Odpowiedź:
W PostgreSQL domyślnie używany jest poziom Read Committed — w nim transakcja widzi tylko zatwierdzone w momencie zapytania dane, ale możliwe są "niepowtarzające się odczyty" (non-repeatable reads).
W MySQL (InnoDB) — Repeatable Read. Różnica od Serializable polega na tym, że tylko ostatni całkowicie zapobiega jakimkolwiek fantomowym lub równoległym zmianom, ale działa znacznie wolniej z powodu globalnych blokad.

Przykład:

-- W Repeatable Read SELECT może zwrócić te same wiersze, a w Read Committed — mogą pojawić się nowe między dwoma SELECT w jednej transakcji.

Historia

W dużym systemie finansowym podczas masowych przelewów między kontami przy niskim poziomie izolacji (Read Committed) okresowo pojawiały się sytuacje, gdy ten sam bilans był wykorzystywany jednocześnie przez kilka transakcji. Prowadziło to do podwójnego wydawania środków (race condition). Po przejściu na Serializable i odpowiednim zarządzaniu blokadami problem zniknął.


Historia

W e-commerce transakcja z UPDATE product SET stock = stock - 1 bez obłożenia jej w transakcji prowadziła do sprzedaży większej ilości towarów, niż jest w magazynie. Problem ujawnił się tylko przy dużej liczbie konkurencyjnych zamówień. Rozwiązanie — wykorzystanie transakcji i blokowań wierszy przez SELECT ... FOR UPDATE.


Historia

W systemie logistycznym w jednej z tabel podczas częstych aktualizacji zapomniano o jawnej komendzie commit. W przypadku awarii część danych została utracona z powodu autokomitu lub nieprawidłowego rollback. Efekt — utrata zapisów i kosztowny audyt.