Zagadnienia

7. Transakcje i współbieżność

7.1. Współbieżność

Współbieżność działania to cecha wszystkich współczesnych systemów komputerowych (pierwsze komputery pracowały sekwencyjnie, co oszczędzało wielu kłopotów, lecz niestety było dość wolne).

Przykład wsoółbieżnych działań: dwie osoby równocześnie pobierają 500 złotych z tego samego konta używając (różnych) bankomatów. System bazy danych powinien zadbać, żeby obie operacje zostały odnotowane, tzn. stan konta należy zmniejszyć dwukrotnie.

Podobna sytuacja występuje w innych programach. W wielu prostych edytorach tekstu jeśli dwie osoby równocześnie modyfikują ten sam dokument, utrwalone zostają tylko zmiany jednej z nich. Obecnie jest to jednak rzadko obserwowane, bo pracuje się głównie na komputerach osobistych — użytkownicy jawnie przesyłają sobie dokument.

Ale pamiętajmy, że istnieją rozproszone systemy plików (choćby NFS) i np. w laboratorium studenckim z serwerami plików może się zdarzyć, że dwie osoby będą modyfikować ten sam plik.

7.2. Transakcje

Transakcje to jedno z podstawowych pojęć współczesnych systemów baz danych. Umożliwiają one współbieżny dostęp do zawartości bazy danych, dostarczając niezbędnych mechanizmów synchronizacji.

Istotą transakcji jest integrowanie kilku operacji w jedną niepodzielną całość.

Popatrzmy na przykład wymagającu użycia transakcji. W systemie bankowym jest wykonywany przelew 100 złp z konta bankowego jednego klienta (np. Kangurzycy) na konto innego klienta (np. Tygrysa). W SQL wygląda to zapewne następująco

UPDATE Konta SET saldo = saldo - 100.00
    WHERE klient = 'Kangurzyca';
UPDATE Konta SET saldo = saldo + 100.00
    WHERE klient = 'Tygrys';

Co stanie się, jeśli po wykonaniu pierwszego polecenia nastąpi awaria dysku?

Podobny problem występuje nawet przy pojedynczym poleceniu SQL, jeśli modyfikuje ono wiele wierszy.

Rozwiązanie takich problemów to transakcyjny system baz danych. Gwarantuje on zapisanie modyfikacji w sposób trwały przed zakończeniem transakcji, a jeśli się nie uda to transakcja jest wycofywana.

7.2.1. Semantyka bazy danych

Semantykę bazy danych określa się opisując zbiór legalnych stanów, czyli ograniczając dozwoloną zawartość tabel.

Operacje modeluje się jako funkcje:

operacja: Stan Stan

Operacje powinny przeprowadzać legalne stany w legalne stany. Jednak w czasie wykonywania powiązanego ciągu operacji przejściowo baza danych może przyjmować nielegalne stany. Takie ciągi obudowujemy transakcjami i traktujemy transakcje jako niepodzielne operacje.

7.2.2. Transakcja

Transakcja to ciąg operacji do wspólnego niepodzielnego wykonania.

Współbieżne wykonywanie transakcji wymaga zachowania własności ACID (Atomicity, Consistency, Isolation, Durability):

  • niepodzielności: ,,wszystko-lub-nic”, transakcja nie może być wykonana częściowo;

  • integralności: po zatwierdzeniu transakcji muszą być spełnione wszystkie warunki poprawności nałożone na bazę danych;

  • izolacji: efekt równoległego wykonania dwu lub więcej transakcji musi być szeregowalny;

  • trwałości: po udanym zakończeniu transakcji jej efekty na stałe pozostają w bazie danych.

7.2.3. Wycofanie i zatwierdzenie transakcji

W trakcie wykonywania transakcja może być wycofana w dowolnym momencie. Wszelkie wprowadzone przez nią zmiany danych zostaną wtedy zignorowane.

Realizacja tego wymaga ,,tymczasowego” wykonywania transakcji. Zmiany danych są tylko obliczane i zapisywane w specjalnym dzienniku transakcji.

Po zakończeniu wykonywania transakcji następuje jej zatwierdzenie, w wyniku czego zmiany są utrwalane w bazie danych.

Konflikty współbieżności

  • Odwiedźmy bazę danych piwiarni i zajmijmy się tabelą Sprzedaje(bar,piwo,cena).

  • Przypuśćmy, żę w barze ,,U Szwejka” sprzedaje się tylko dwa gatunki piwa: Okocim po 2,50 zł i Żywiec po 3,50 zł.

  • Dzielny redaktor gazety postanowił zbadać (używając naszej bazy danych), jaka jest najwyższa i najniższa cena piwa ,,U Szwejka”.

  • W tym samym czasie szef piwiarni zdecydował, że przestaje sprzedawać dotychczasowe piwa i przerzuci się na Heineken po 4,50 zł.

  • Pan redaktor wykonuje dwa następujące zapytania (po lewej stronie ich umowne nazwy)

    (max) SELECT MAX(cena) FROM Sprzedaje
    WHERE bar = 'U Szwejka';
    (min) SELECT MIN(cena) FROM Sprzedaje
    WHERE bar = 'U Szwejka';
  • A ,,równocześnie” szef piwiarni wykonał dwa inne polecenia SQL

    (del) DELETE FROM Sprzedaje
    WHERE bar = 'U Szwejka';
    (ins) INSERT INTO Sprzedaje
    VALUES('U Szwejka','Heineken',4.50);

Przeplecione polecenia

  • Przypuśćmy, że powyższe polecenia zostały wykonane w następującej kolejności: max, del, ins, min.

  • Popatrzmy na efekty:

    Ceny Operacja Wynik
    {2.50,3.50} max 3,50
    {2.50,3.50} del
    {} ins
    {4.50} min 4,50
  • A więc ostatecznie MAX(…) < MIN(…)!

  • Aby tego uniknąć, powinniśmy operacje poszczególnych osób pogrupować w transakcje.

  • Wtedy obie operacje pana redaktora wykonają się bezpośrednio po sobie, nie wiadomo tylko, czy przed, czy po zmianie ,,repertuaru”.

Problem wycofywania

  • Szef piwiarni po wykonaniu (bez użycia transakcji) ciągu operacji (del)(ins) postanowił wycofać drugą z nich (ROLLBACK)

  • Jeśli redaktorowi udało się ,,wstrzelić” zapytanie między (ins) i ROLLBACK, zobaczy wartość (4,50), której nigdy nie było w bazie danych.

  • Rozwiązaniem jest znowu użycie transakcji:

    • Efekty transakcji nie są widziane przez innych, dopóki transakcja nie zostanie zatwierdzona (COMMIT).

Blokady

  • Dla zapobiegania konfliktom używa się wewnętrznie blokowania dostępu do elementów danych używanych przez transakcję.

  • Poziomy ziarnistości blokad:

    • cała baza danych,

    • pojedyncza relacja,

    • blok wierszy,

    • pojedynczy wiersz.

Rodzaje transakcji

  • Bezpośrednie (direct);

  • Konwersacyjne — kilkakrotna wymiana informacji klient/serwer;

  • Wiązane (chained) – wymagają przechowywania kontekstu;

  • Zagnieżdżone

  • Długotrwałe.

  • Kolejkowane – wykonywane z opóźnieniem, np. w celu grupowania;

7.3. Transakcje w SQL

Początek transakcji jest zwykle domyślny — pierwsza operacja na bazie danych wykonana przez aplikację.

W Postgresie przez można podać jawnie

BEGIN [WORK]

co jest najczęściej używane do wyjścia z trybu autocommit podczas pracy interakcyjnej.

Zakończenie transakcji następuje przez zatwierdzenie

COMMIT;

lub anulowanie (wycofanie)

ROLLBACK;

Uwaga: przy wystąpieniu błędu (np. naruszenie ograniczeń) ma miejsce niejawne wycofanie transakcji.

Domyślnie transakcje zezwalają na zapis. Rezygnuje się jednak z tego np. wtedy, gdy chcemy dokonać dłuższego skomplikowanego przeszukania spójnego stanu bazy danych.

Transakcję należy wtedy poprzedzić deklaracją

SET TRANSACTION LEVEL READ ONLY;

W transakcji takiej nie mogą wystąpić operacje modyfikacji, ale za to nie są widoczne zmiany dokonywane przez inne współbieżne transakcje.

Domyślnie przyjmowany jest poziom READ WRITE, tak jak gdyby podano

SET TRANSACTION LEVEL READ WRITE;

7.4. Poziomy izolacji transakcji

Poziom izolacji dla transakcji ustalamy korzystając z polecenia

SET TRANSACTION ISOLATION LEVEL
    [READ COMMITTED | SERIALIZABLE];

Poziom izolacji opisuje tylko, jak dana transakcja chce widzieć bazę danych (nie dotyczy więc bezpośrednio innych transakcji).

Poziom izolacji SERIALIZABLE gwarantuje semantykę sekwencyjną dla transakcji (ACID) przez wycofywanie transakcji naruszajacych ją.

Poziom READ COMMITTED powoduje przy modyfikacjach czekanie na zwolnienie (jawnej lub ukrytej) blokady wierszy. Odczyt nie jest jednak powtarzalny: kilka kolejnych wywołań tego samego zapytania w ramach tej samej transakcji może dać różne wyniki, ponieważ transakcja ma dostęp do wszystkich zatwierdzonych już modyfikacji z innych transakcji.

7.4.1. Transakcje w SQL

Standard dopuszcza również poziomy izolacji:

  • REPEATABLE READ, gdy odczyty w ramach transakcji dają zawsze te same wiersze co poprzednio, ale mogą się pojawić dodatkowe wiersze: ,,fantomy”. Żadne wiersze nie mogą jednak zniknąć.

  • READ UNCOMMITED, zezwalający na tzw. brudne odczyty (dirty reads): odczytanie danych zmodyfikowanych przez inną transakcję, która potem zostaje wycofana.

    W tym przypadku domyślnym poziomem transakcji jest READ ONLY, ponieważ READ WRITE jest na ogół zbyt ryzykowny.

7.5. Blokady w Oracle

  • Blokady można zakładać na całą tabelę

    LOCK TABLE tabela
    IN [SHARE | EXCLUSIVE] MODE
    [NOWAIT];
    
  • SHARE oznacza blokadę dzieloną (tylko przeciw zmianom).

  • EXCLUSIVE to wyłączna blokada dostępu (w celu dokonania zmian).

  • NOWAIT chroni przed czekaniem, gdy nie można natychmiast założyć blokady.

  • Zdjęcie blokad następuje przez wykonanie COMMIT lub ROLLBACK.

  • Lepiej jednak zakładać blokady na wybrane wiersze, np. gdy transakcja odczytuje pewne wiersze, a następnie dokonuje (zwykle w nich) zmian, można użyć

    SELECT ... FOR UPDATE [NOWAIT];
    
  • Taka blokada też jest ważna do końca transakcji.

Realizacja transakcji

  • Dziennik transakcji do zapisywania wszystkich operacji.

  • Rejestrowanie wycofań w dzienniku.

  • Podczas odtwarzania powtarzamy tylko operacje z zatwierdzonych transakcji.

Znaczniki czasowe

  • Inne podejście, dobre gdy głównie odczyty.

  • Optymistyczne, wycofanie transakcji gdy konflikt = fizycznie niemożliwy ciąg (lock wycofuje tylko gdy blokada).

  • Transakcja utożsamiana z momentem startu.

Treść automatycznie generowana z plików źródłowych LaTeXa za pomocą oprogramowania wykorzystującego LaTeXML.

Projekt współfinansowany przez Unię Europejską w ramach Europejskiego Funduszu Społecznego.

Projekt współfinansowany przez Ministerstwo Nauki i Szkolnictwa Wyższego i przez Uniwersytet Warszawski.