jak działają transakcje – systemy OLTP

By zapewnić dostępność danych w systemie transakcyjnym (czyli takim w którym użytkownicy modyfikuj dane), należy porozmyślać trochę nad samym systemem. Załóżmy że pracujemy na systemie przy którym równolegle na różnych stanowiskach pracuje 5 fakturzystek. Jest to system do wypisywania faktur i obsługi stanów magazynowych. Dla pełni obrazu ustalmy model iż klient najpierw przychodzi składa zamówienie na podstawie którego powstaje faktura. Klient z fakturą udaje się na magazyn gdzie towar jest kompletowany i wydawany. Model ten jest dużym uproszczeniem, ale pozwoli przedstawić idee transakcji.

Patrząc atomowo na proces wypisywania faktury:
musimy wystawić nagłówek i zacząć dodawać pozycje.
Dodanie pozycji to wpisanie jej na fakturę plus zmniejszenie stanów magazynowych + sprawdzenie linii kredytowej klienta (przekroczenie linii powoduje niemożność dodania kolejnej pozycji do faktury).

Na dodaniu pozycji się skupimy. Wyraźnie widać że musimy przeprowadzić kilka operacji za jednym zamachem.
Musimy sprawdzić stan, jeśli jest wystarczający pobrać go na fakturę i zmniejszyć na magazynie itd…

Kilka operacji na bazie które stają się integralnym procesem powinno przeprowadzić się w transakcji. Transakcja jest rozliczana zero jedynkowo (sukces / porażka), w przypadku porażki baza powraca do stanu sprzed uruchomienia transakcji(mówimy o poziomie modyfikowanych danych). Dodatkowo transakcja alokuje nam zasoby a w wielu przypadkach wręcz je blokuje (o czym dokładniej się rozpiszę za chwilę). Najważniejsze jest jednak że transakcja nas chroni. Ochrona ta polega na tym iż wykonując krokowo te czynności może w trakcie dojść do styuacji gdy pójdzie coś nie tak. Zdejmiemy stan ale nigdzie go nie wstawimy. Patrząc z perspektywy banku i przelewu zdejmiemy pieniądze z jednego konta ale ich nigdzie nie przelejemy, lub na odwrót wstawimy pieniądze na konto i ich gdzieś nie zdejmiemy. Takie sytuacje są bardzo niebezpieczne. Dlatego też jeśli w jednej operacji korzystamy z więcej niż jednego zapytania musimy to objąć w transakcje.
W większości systemów wygląda to tak:

begin transaction
update tabela1 set pole1=23 where id=23;
insert into tabela2 set pol2=34 where id=324435;
commit;
end transaction;

Np w Oracle zaczynając pierwszą operację DML ustanawiamy transakcję i będzie ona trwać do momentu
wydania polecenia commit/rollback. Dla nowych użytkowników jest to dość zaskakujące i może się skończyć zlokowaniem tabel. W momencie gdy w Oracle aktualizujemy dane (otwieramy transakcje) pozostałe transakcje dla tej tabeli są wstrzymane (czekają w kolejce). Pozostali użytkownicy mogą w tym czasie pobierać dane (robić selecty) jednak ujrzą dane sprzed otwarcia transakcji. Objawem otwartje transakcji jest np dodanie pozycji do faktury które zawiesza program, albo wykonanie zapytania które wykonuje się w ułamek sekundy (normalnie), a tym razem się wykonuje bez końca. W Oracle są odpowiednie widoki, które pozwalają przejrzeć locki na tabelach. W przypadku MSSQL sprawa wygląda zupełnie inaczej. Musimy napisać sami że chcemy coś zapiąć w transakcję. Jeśli tego nie zrobimy, czekają nas 2 poważne konsekwencje. Z jednej strony brak gwarancji iż całość przebiegnie poprawnie. Z drugiej strony nie możliwości cofnięcia transakcji. gdy zmiany się dokonają i efekt nas nie zadowala to musimy napisać zapytania które przywrócą stann poprzedni co nie zawsze jest możliwe. Kolejną różnicą jest fakt iż w MSSQL abele są blokowane już przy pobieraniu danych. Więc tworząc raport który wykonuje np 10 min. Na te 10 min blokujemy tabele z których tworzy się raport. Na szczęście czasem MSSQL wykorzystuje bazę tymczasową gdzie wrzuca kolejne kroki zapytań zwalniając zasoby. Tak więc operując na wielu tabelach możemy skrócić czas blokowania tabel. Dodatkowym bonusem od Billa jest składnia with (nolock).

select * from tabela with (nolock)

Pozwala to na wykonanie zapytania które nie zablokuje tabeli. Niestety musimy liczyć się z tym iż w wyniku dostaniemy nie aktualne dane. Jednak np dla raportów za minione okresy jest to stosunkowo dobre rozwiązanie.

Ew. Gdy zależy nam na jakiś konkretnych danych mozemy tylko część tabel zrobić z with nolock, a najważniejsze dane pobać bez tego parametru:

select * from tabela1 t
inner join tabela t2 with(nolock) on
t1.id = t2.id;

Jak się ma sprawa w MYSQL? To już zupełnie inna historia. W mysql mamy do wyboru różne silniki. Istnieją silniki które nie wspierają transakcji. W przypadku wsparcia dla transakcji nie wiem jaki jest ich wpływ na locki na tabelach.

Istnieje także pojęcie transakcji rozproszonych na różnych maszynach. W przypadku linku 2 mssqli lub nawet 2 baz na jednej instancji, jesteśmy w stanie przeprowadzić sensowną transakcję. Wymaga to pewnej prekonifguracji ale jest to osiągalne. W przypadku Oracle na zdalnym serwerze (linkowanym) Nie ma możliwości przeprowadzenia DML.
Jeśli chodzi o środowiska heterogeniczne (MSSQL i ORACLE). W przypadku korzystania z OpenQuery nie jesteśmy w stanie przeprowadzić sensownej transakcji. Openquery wymusza commit po każdym zapytaniu. Można skorzystać z dodatkowych pól w tabelach do oznaczania statusów ale to już zabawa.

W oracle mamy jeszcze transakcje autonomiczne. Jest to transakcja w transakcji. Przydatne zwłaszcza do logowanai błędów. Cofamy główną transakcję a pod spodem zapisujemy informacje o błędzie. Bardzo przydatna sprawa!

Istnieje jeszcze pojęcie stopnia izolacji transakcji ale to już zagłębianie się w szczegóły.

Leave a Reply

Your email address will not be published. Required fields are marked *

4 × three =