Na naszym kursie będziemy omawiać transakcje aż trzy razy. I za każdym razem odkryjemy coś nowego. Teraz ogarniemy podstawy transakcji. W drugiej połowie kursu zajmiemy się poziomami izolacji transakcji, a na sam koniec — niuansami pracy z transakcjami zagnieżdżonymi.
Czym jest transakcja
Zacznijmy od prostego — definicji. Transakcja to grupa działań, które wykonują się tylko razem. Wszystko albo nic.
Z tego wynika ważny wniosek: jeśli podczas wykonywania jakiejkolwiek (dowolnej!) akcji z grupy coś pójdzie nie tak, trzeba cofnąć efekty wszystkich pozostałych działań i przywrócić system do stanu początkowego.
Z punktu widzenia SQL, transakcja to zestaw działań (np. wstawianie, aktualizacja lub usuwanie danych), który gwarantuje, że albo wszystkie operacje w transakcji zakończą się sukcesem, albo żadna nie zostanie wykonana. Takie zachowanie sprawia, że praca z bazami danych jest niezawodna i spójna, szczególnie w krytycznych sytuacjach, jak obsługa płatności czy aktualizacja powiązanych danych.
Wyobraź sobie, że przelewasz kasę między dwoma kontami bankowymi. Upraszczając, mamy dwie następujące po sobie operacje:
- Z jednego konta pobierana jest kwota.
- Na drugie konto wpłacana jest ta sama kwota.
Jeśli coś się wysypie na samym końcu (np. padnie serwer), ważne, żeby żaden z użytkowników nie był stratny. Innymi słowy, albo oba kroki się udają (transakcja jest zatwierdzona), albo oba są cofnięte (transakcja anulowana).
Transakcje i zasady ACID
Transakcje opierają się na zasadach ACID, które przypominają nam o czterech kluczowych cechach:
- Atomicity (Atomowość): wszystko albo nic. Albo wszystkie operacje w transakcji się wykonają, albo zostaną cofnięte.
- Consistency (Spójność): dane pozostają poprawne przed i po transakcji.
- Isolation (Izolacja): każda transakcja działa tak, jakby była jedyna w systemie.
- Durability (Trwałość): po zatwierdzeniu dane są zapisane, nawet jeśli serwer padnie.
Podstawowe komendy do zarządzania transakcjami
No to czas na praktykę! Oto trzy najważniejsze komendy do zarządzania transakcjami:
BEGIN
Rozpoczyna nową transakcję. Wszystkie kolejne operacje będą wykonywane w jej ramach.COMMIT
Zatwierdza zmiany. Po tej komendzie wszystkie działania stają się trwałe.ROLLBACK
Cofnij zmiany. Jeśli coś pójdzie nie tak, możesz cofnąć transakcję i dane wrócą do stanu początkowego.
Podstawowa składnia transakcji
Prosta struktura transakcji:
BEGIN;
-- tutaj idą twoje operacje SQL
COMMIT;
Przykład użycia ROLLBACK:
BEGIN;
-- zmiana tabeli students
UPDATE students
SET grade = grade + 10
WHERE id = 1;
-- O! Zorientowaliśmy się, że to był błąd.
ROLLBACK;
Przykład użycia transakcji w realnym zadaniu
Załóżmy, że mamy tabele:
students:
| id | name | grade |
|---|---|---|
| 1 | Otto Lin | 85 |
| 2 | Anna Song | 90 |
courses:
| course_id | course_name |
|---|---|
| 1 | Matematyka |
| 2 | Historia |
Załóżmy, że chcemy jednocześnie zapisać studenta na kurs i zaktualizować jego średnią ocenę:
BEGIN;
-- Krok 1: Dodajemy wpis do tabeli "zapisy na kursy"
INSERT INTO course_enrollments (student_id, course_id)
VALUES (1, 2);
-- Krok 2: Aktualizujemy średnią ocenę studenta
UPDATE students
SET grade = grade + 5
WHERE id = 1;
COMMIT;
Co się stanie, jeśli serwer padnie między pierwszym a drugim krokiem? Gdybyśmy nie użyli transakcji, dane byłyby niespójne: zapis na kurs się doda, ale średnia ocena nie zostanie zaktualizowana. Ale z transakcją obie operacje wykonają się razem albo żadna.
Obsługa błędów w transakcjach
Czasem coś pójdzie nie tak i trzeba ogarnąć błąd. W PostgreSQL transakcja automatycznie się cofnie, jeśli wystąpi błąd.
Zróbmy błąd specjalnie i zobaczmy, co się stanie. Wyobraź sobie, że mamy ograniczenie unikalności na kolumnę student_id w tabeli course_enrollments. Spróbujmy dodać zduplikowany wiersz:
BEGIN;
INSERT INTO course_enrollments (student_id, course_id)
VALUES (1, 2);
-- KONIEC TRANSAKCJI (jeszcze nie wykonane)
COMMIT;
Jeśli spróbujesz dodać studenta, który już jest zapisany na kurs, pojawi się błąd i PostgreSQL automatycznie zakończy transakcję cofając ją.
Użycie ROLLBACK do ręcznego cofania
Często nie da się przewidzieć błędów i będziesz chciał cofnąć transakcję, jeśli coś pójdzie nie tak:
BEGIN;
-- Dodajemy nowego studenta
INSERT INTO students (name, grade)
VALUES ('Omori Sanny', 75);
-- Ups! Zorientowaliśmy się, że studenta dodaliśmy przez przypadek.
ROLLBACK;
Po komendzie ROLLBACK tabela zostaje bez zmian — Omori Sanny nie pojawił się w students.
Przydatne wskazówki i typowe błędy
Praca z transakcjami jest prostsza, jeśli pamiętasz o kilku ważnych zasadach:
- Zawsze używaj transakcji, jeśli twoja operacja ma więcej niż jeden krok, szczególnie przy modyfikacji danych w kilku tabelach.
- Nigdy nie zapominaj zatwierdzić zmian (
COMMIT). W przeciwnym razie transakcja zostanie niedokończona, a dane się nie zmienią. - Opakuj skomplikowane operacje w transakcje, żeby zachować spójność danych.
- Jeśli widzisz błąd na którymkolwiek etapie, nie bój się użyć
ROLLBACK.
Teraz, gdy już wiesz, jak kontrolować operacje za pomocą transakcji, czas wykorzystać tę wiedzę w praktyce i przejść do nauki transakcji dla zapewnienia integralności danych!
GO TO FULL VERSION