Cześć wszystkim. Z tej strony Władysław Rodin. Obecnie jestem kierownikiem kursu „Architekt wysokich obciążeń” w OTUS i uczę na kursach dotyczących architektury oprogramowania.
Oprócz nauczania, jak mogliście zauważyć, zajmuję się pisaniem autorskiego materiału dla bloga OTUS na Habrze, a dzisiejszy artykuł chcę poświęcić uruchomieniu kursu , na który obecnie trwa rekrutacja.

Wprowadzenie
W Rozmawialiśmy o tym, że transakcje w bazach danych służą do rozwiązania dwóch zadań: zapewnienia odporności na błędy i dostępu do danych w konkurencyjnym środowisku. Aby w pełni realizować te zadania, transakcja musi mieć właściwości ACID. Dziś szczegółowo porozmawiamy o literze I (izolacja) w tym skrócie.
Izolacja
Izolacja rozwiązuje problem dostępu do danych w konkurencyjnym środowisku, faktycznie zapewniając ochronę przed warunkami wyścigu. W idealnym przypadku izolacja oznacza serializację, czyli właściwość, która zapewnia, że wynik wykonywania transakcji równolegle jest taki sam, jak gdyby były wykonywane sekwencyjnie. Główny problem tej właściwości polega na tym, że jest bardzo trudna do technicznego zapewnienia, a w konsekwencji mocno obciąża wydajność systemu. Dlatego izolację często osłabia się, podejmując ryzyko wystąpienia niektórych anomalii, o których mowa poniżej. Możliwość wystąpienia różnych anomalii charakteryzuje właśnie poziom izolacji transakcji.
Najbardziej znane anomalie to: brudne odczyty, niepowtarzalne odczyty, fantomowe odczyty, ale tak naprawdę jest ich jeszcze 5: brudne zapisy, utrata kursora, utrata aktualizacji, rozrzut odczytu, rozrzut zapisu.
Brudny zapis
Istota anomalii polega na tym, że transakcje mogą nadpisywać niezakończone dane.

Ta anomalia jest niebezpieczna nie tylko dlatego, że dane mogą konfliktować po zatwierdzeniu obu transakcji (jak na obrazku), ale również dlatego, że narusza atomowość: ponieważ zezwalamy na nadpisywanie niezakończonych danych, nie jest jasne, jak cofnąć jedną transakcję, nie wpływając na drugą.
Naprawienie anomalii jest dość proste: nakładamy blokadę na zapis przed rozpoczęciem zapisu, zabraniając innym transakcjom zmieniać zapis, dopóki blokada nie zostanie zdjęta.
Brudny odczyt
Dirty read oznacza odczyt niezatwierdzonych danych.

Problemy występują, gdy na podstawie próby konieczne jest podjęcie jakichś działań lub podjęcie decyzji.
Aby naprawić anomalię, można wprowadzić blokadę na odczyt, ale to znacznie wpłynie na wydajność. Dużo łatwiej jest powiedzieć, że dla rollback'u transakcji pierwotny stan danych (przed rozpoczęciem zapisu) musi być zachowany w systemie. Czemu by nie odczytać stamtąd? To wystarczająco tanie, dlatego większość baz danych wyłącza dirty read domyślnie.
Lost update
Lost update oznacza utracone aktualizacje, a tłumaczenie dość dokładnie oddaje istotę problemu:

W rzeczywistości wynik transakcji T2 został cofnięty. Tę sytuację można naprawić jawymi lub niejawymi blokadami zapisu. To znaczy, albo po prostu wykonujemy aktualizację zapisu, a wtedy zachodzi niejawna blokada, albo wykonujemy select for update, co powoduje powstanie blokady na odczycie i zapisie. Zauważ, że taka operacja jest dość niebezpieczna: swoim "niewinnym" odczytem blokujemy inne odczyty. Niektóre bazy oferują bardziej bezpieczne select for share, które pozwala odczytywać dane, ale nie pozwala ich zmieniać.
Cursor lost update
Aby uzyskać bardziej szczegółową kontrolę, bazy mogą oferować inne narzędzia, na przykład kursor. Kursor to struktura zawierająca zbiór wierszy, która pozwala na iterację po nich. declare cursor_name for select_statement. Zawartość kursora opisuje select.
Po co potrzebny jest kursor? Chodzi o to, że niektóre bazy danych oferują blokadę na wszystkie zapisy wybrane przez select (stabilność odczytu), albo tylko na ten zapis, na którym aktualnie znajduje się kursor (stabilność kursora). Przy stabilności kursora realizowana jest krótka blokada, co pozwala zredukować liczbę blokad w sytuacji, gdy iterujemy po dużej próbie danych. Dlatego anomalia lost update jest wyróżniana dla kursora osobno.
Non-repeatable read
Non-repeatable read polega na tym, że podczas wykonywania naszej transakcji 2 kolejne odczyty tego samego zapisu spowodują uzyskanie różnych wyników, ponieważ inna transakcja wtrąciła się pomiędzy tymi dwoma odczytami, zmieniła nasze dane i została zatwierdzona.

Dlaczego to w ogóle jest problemem? Wyobraź sobie, że celem transakcji T2 na rysunku jest wybranie wszystkich produktów, których cena jest niższa niż 150 jednostek. Ktoś inny zaktualizował cenę do 200 jednostek. W ten sposób ustalony filtr nie zadziałał.
Dane anomalii przestają występować po dodaniu blokad dwuetapowych lub po zastosowaniu mechanizmu MVCC, o czym chciałbym porozmawiać osobno.
Odczyt phantomowy
Odczyt phantomowy odnosi się do odczytu danych, które zostały dodane przez inną transakcję.

Na przykład można zaobserwować błędny wybór najtańszego produktu w przypadku wystąpienia tej anomalii.
Pozbycie się odczytów phantomowych jest już wystarczająco trudne. Zwykła blokada nie wystarcza, ponieważ nie możemy zablokować tego, co jeszcze nie istnieje. Systemy 2PL korzystają z blokady predykatywnej, podczas gdy systemy MVCC, planista transakcji, anuluje transakcje, które mogą być naruszone przez wstawienie. Zarówno jeden, jak i drugi mechanizm są wystarczająco zasobożerne.
Odczyt skew
Odczyt skew występuje, gdy pracujemy z wieloma tabelami, których zawartość powinna zmieniać się w sposób spójny.
Załóżmy, że istnieją tabele reprezentujące posty i ich metainformacje:

Jedna transakcja odczytuje z tabel, a druga je zmienia:

W wyniku wykonania transakcji T1, post o tytule = Dobry, a updated_by = T2, co stanowi pewne niezgodności.
W rzeczywistości jest to niepowtarzalny odczyt, ale w ramach kilku tabel.
Aby to naprawić, T1 może nałożyć blokady na wszystkie wiersze, które będzie odczytywać, co uniemożliwi transakcji T2 zmianę informacji. W przypadku MVCC transakcja T2 zostanie anulowana. Ochrona przed tą anomalią może być ważna, jeśli korzystamy z kursorów.
Odczyt skew
Tę anomalię również łatwiej wyjaśnić na przykładzie: załóżmy, że w naszym systemie przynajmniej jeden doktor musi być na dyżurze, ale obaj lekarze postanowili odwołać swoje dyżury:


Anomalia doprowadziła do tego, że żaden z lekarzy nie wystąpi na dyżur. Dlaczego tak się stało? Ponieważ transakcja sprawdziła warunek, który mógł zostać naruszony przez inną transakcję, a z powodu izolacji nie zauważyliśmy tej zmiany.
To ten sam niepowtarzalny odczyt. Jeśli to możliwe, selekcje mogą nakładać blokady na te rekordy.
Write skew i read skew to kombinacje wcześniejszych anomalii. Można rozważyć write skew jako de facto phantom read. Weźmy pod uwagę tabelę, w której znajdują się imiona pracowników, ich pensje oraz projekty, nad którymi pracują:


Ostatecznie otrzymujemy następujący obraz: każdy menadżer myślał, że jego zmiana nie wywoła przekroczenia budżetu, w związku z czym wprowadzili zmiany kadrowe, które w sumie doprowadziły do nadwyżki wydatków.
Przyczyna wystąpienia problemu jest dokładnie taka sama jak w przypadku phantom reading.
Wnioski
Osłabienie poziomu izolacji transakcji w bazie danych stanowi kompromis pomiędzy bezpieczeństwem a wydajnością, a wybór tego poziomu powinien być uzależniony od potencjalnych ryzyk dla biznesu w przypadku wystąpienia poszczególnych anomalii.
Źródło: habr.com
