Database mvcc - ghdrako/doc_snipets GitHub Wiki
MVCC to akronim od MultiVersion Concurrency Control (kontrola współbieżności wielu wersji). Zasada polega na ułatwieniu współbieżnego dostępu do bazy danych wielu użytkownikom (sesjom) poprzez stałe posiadanie kilku różnych wersji tego samego rekordu. Każda sesja może pracować jednocześnie na wersji, która ma zastosowanie w jej kontekście (nazywa się to „migawką”). Na przykład, transakcja modyfikująca rekord utworzy nową wersję tego rekordu. Rekord ten nie może być jednak widoczny dla innych transakcji, dopóki modyfikacja nie zostanie zatwierdzona w bazie danych. Inne transakcje zobaczą zatem starą wersję tego rekordu. Termin techniczny to „spójny odczyt”. Należy pamiętać, że szczegółowość modyfikacji odpowiada rekordowi (lub wierszowi) tabeli. Modyfikacja pola (kolumny) jest równoważna modyfikacji wiersza. Dwie transakcje nie mogą modyfikować dwóch różnych pól tego samego rekordu bez powodowania konfliktu, a blokady zawsze dotyczą całych wierszy.
Inne rozwiazania
- Transakcja, która chce wyświetlić rekord, musi go zablokować (aby mieć pewność, że nie zostanie zmodyfikowany) w sposób współdzielony, wyświetlić go, a następnie odblokować. –
- Transakcja, która chce zmodyfikować rekord, musi go zablokować na wyłączność (nikt inny nie powinien mieć możliwości jego modyfikacji ani wyświetlenia), zmodyfikować go, a następnie odblokować.
To rozwiązanie ma zaletę prostoty: menedżer blokad wystarcza do zarządzania współbieżnym dostępem do danych. Ma również zaletę wydajności, ponieważ w przypadku niewielkiej liczby oczekujących na blokadę, opłata za blokadę jest niewielka. Ma jednak wady:
- Blokady są w pamięci. Ich liczba jest zatem prawdopodobnie ograniczona. Co się stanie, jeśli transakcja będzie musiała zablokować 10 milionów rekordów? Zaimplementowano mechanizmy promocji blokad. Blokady wierszy stają się blokadami bloków, a następnie blokadami tabel. Liczba blokad jest ograniczona, a promocja blokad może mieć dramatyczne konsekwencje:
- Proces, który musi odczytać rekord, będzie musiał czekać na jego modyfikację. To szybko prowadzi do poważnych problemów z konfliktami. Zapisujący blokują odczytujących, a odczytujący blokują zapisujących. Oczywiście, autorzy blokują się nawzajem, ale to normalne (nie jest możliwe, aby dwie transakcje modyfikowały ten sam rekord jednocześnie, każda nie wiedząc, co zrobiła druga);
- Instrukcja SQL (zwłaszcza jeśli trwa długo) nie daje gwarancji, że dane będą spójne od początku do końca: jeśli na przykład podczas długiego polecenia SELECT autor zmodyfikuje zarówno dane już odczytane przez polecenie SELECT, jak i dane, które zamierza odczytać, polecenie SELECT nie będzie miało spójnego widoku tabeli. Na przykład w tabeli księgowej może pojawić się fałszywa suma, ponieważ polecenie SELECT zobaczyło tylko część danych zweryfikowanych przez nową transakcję;
- Jak anulować transakcję? Musi istnieć sposób na cofnięcie tego, co zrobiła transakcja, na wypadek gdyby nie zakończyła się ona walidacją, lecz anulowaniem.
Implementacja MVCC za pomocą UNDO
Oto na przykład implementacja Oracle. Gdy rekord wymaga modyfikacji, jest on kopiowany z powrotem do przestrzeni tabel UNDO. Nowa wersja rekordu jest następnie nadpisywana.
Implementuje to MVCC (starsze wersje rekordu są nadal dostępne) i ma kilka zalet:
- Rekordy nie są duplikowane w tabeli. Tabela zatem nie powiększa się po aktualizacji (jeśli nowa wersja nie jest większa od poprzedniej);
- Rekordy zachowują ten sam adres fizyczny w tabeli. Indeksy odpowiadające niezmodyfikowanym danym w rekordzie nie muszą być zatem modyfikowane, ponieważ indeksy umożliwiają znalezienie adresu fizycznego rekordu względem wartości.
Ma to również wady:
- Zarządzanie cofaniem jest bardzo złożone: jak zdecydować, co można usunąć? Czasami czyszczenie jest zbyt agresywne, a transakcje nie mają już dostępu do starych rekordów (na przykład błąd SNAPSHOT TOO OLD w Oracle);
- Spójny odczyt jest skomplikowany w implementacji: dla każdego zmodyfikowanego rekordu konieczne jest posiadanie informacji pozwalających na znalezienie obrazu przed modyfikacją rekordu (i właściwego obrazu, może być ich kilka). Następnie musi być w stanie odtworzyć go w pamięci;
- Trudno jest poprawnie określić rozmiar pliku cofania. Zdarza się również, że jest on zbyt mały, co powoduje anulowanie dużej transakcji. Jest to również potencjalnym źródłem konfliktów między sesjami;
- Wycofywanie jest bardzo powolne: dla wszystkich modyfikacji transakcji konieczne jest cofnięcie pracy, dlatego należy przywrócić obrazy zawarte w pliku cofania, ponownie zastosować je do tabel (co generuje nowe zapisy). Czas anulowania może być dłuższy niż początkowy czas przetwarzania, aby anulować.
Implementacja MVCC w Postgresie
W tabeli PostgreSQL rekord może być przechowywany w wielu wersjach. Modyfikacja rekordu powoduje zapisanie nowej wersji. Starą wersję można ponownie wykorzystać tylko wtedy, gdy żadna transakcja nie będzie jej już potrzebować, tj. żadna transakcja nie ma migawki bazy danych starszej niż operacja modyfikacji tego rekordu, a zatem ta wersja jest niewidoczna dla wszystkich. Każda wersja.
Tabela rekordów oczywiście zawiera informacje pozwalające określić, czy jest ona widoczna w danym kontekście. Zalety tej implementacji, polegającej na przechowywaniu kilku wersji w tabeli głównej, są liczne:
- Spójny odczyt jest bardzo prosty w implementacji: każda sesja odczytuje interesującą ją wersję. Widoczność wersji rekordu jest łatwa do określenia;
- Brak możliwości cofnięcia. To jeden aspekt mniej do zarządzania w administrowaniu bazą danych;
- Brak możliwości cofnięcia;
- Brak kopiowania do cofnięcia przed aktualizacją rekordu. Aktualizacja jest zatem tańsza;
- Anulowanie transakcji jest natychmiastowe: stare rekordy są zawsze dostępne.
Ta implementacja ma pewne wady:
- Przestarzałe wersje rekordów muszą być regularnie usuwane;
- Większa potrzeba konserwacji indeksu (ale mniejsza potrzeba rywalizacji o jego aktualizację);
- Rekordy zawierają informacje o widoczności, co powoduje ich większy rozmiar.