Jest to drugi artykuł z serii poświęconej dziennikowi transakcji SQL Server i jego specyfice. Tutaj sprawdzimy szczegóły rekordu dziennika.
Rekordy dziennika
Rekordy dziennika są podstawą mechanizmów rejestrowania i odzyskiwania. Rekord dziennika opisuje pojedynczą zmianę w bazie danych. Tak więc każda zmiana w bazie danych ma zapis dziennika lub zapisy dziennika, które pomagają opisać tę konkretną zmianę. Chociaż nie musisz rozumieć szczegółów rekordów dziennika, aby zrozumieć, co się dzieje z rejestrowaniem i odzyskiwaniem, te szczegóły są niezwykle interesujące.
Rekord dziennika ma unikalny numer sekwencji dziennika, który zdefiniowaliśmy w pierwszym artykule. Numer sekwencji dziennika umożliwia odnalezienie rekordu dziennika w dzienniku transakcji. Ponadto każda strona pliku danych ma w nagłówku strony numer LSN, który identyfikuje najnowszy rekord dziennika, którego zmiana jest odzwierciedlona na stronie. Ma to kluczowe znaczenie dla odzyskiwania po awarii.
Zapisy dziennika dla jednoczesnych transakcji są przeplatane w dzienniku transakcji zgodnie z datą ich wystąpienia w czasie. Rekordy dziennika są przechowywane w blokach dziennika w puli buforów, dopóki nie zostaną opróżnione na dysk.
W bazach danych użytkowników lub systemowych nie ma operacji niezalogowanych. Jest jednak wyjątek:w tempdb operacje dotyczące magazynu wersji i pliku roboczego nie są rejestrowane. Zapisy dziennika nigdy nie poruszają się w dzienniku transakcji.
Co to jest w zapisie dziennika?
Informacje w rekordzie dziennika umożliwiają jego ponowne wykonanie (wycofanie do przodu) lub cofnięcie (wycofanie). Ta zdolność zapisu dziennika ma kluczowe znaczenie dla umożliwienia wycofywania transakcji i do pracy przy odzyskiwaniu. Rekordy dziennika zawierają wiele pól, w zależności od typu rekordu dziennika. Istnieje kilka pól wspólnych dla wszystkich rekordów, w tym:
- Typ rekordu dziennika
- rozpocznij zapis dziennika transakcji
- zatwierdź zapis dziennika transakcji
- przydzielanie strony poprzez zmianę bitmapy przydziału
- wstawianie wiersza
- usuwanie wiersza
- modyfikowanie wiersza
- Kontekst rekordu dziennika , jeśli w ogóle.
- Identyfikator transakcji, którego częścią jest zapis dziennika Jeśli w ogóle. Większość zapisów dziennika jest częścią transakcji.
- Długość rekordu dziennika . Rekordy dziennika zwykle mają stały rozmiar, a następnie, w zależności od ilości danych w rekordzie dziennika, będzie również zmienna część.
- LSN poprzedniego rekordu dziennika w tej samej transakcji . Jeśli w ogóle. LSN jest zasadniczo wskaźnikiem do poprzedniego rekordu dziennika transakcji, który został wygenerowany przez tę konkretną transakcję. Ten łańcuch poprzednich LSN pozwala na wycofanie tej konkretnej transakcji, ponieważ wycofywanie odbywa się w odwrotnej kolejności, zaczynając od najnowszego rekordu dziennika.
- Ilość zarezerwowanego miejsca w dzienniku na wypadek, gdyby zapis logu musiał zostać cofnięty.
Rezerwacja miejsca w dzienniku
Każdy rekord dziennika, który jest generowany w przedniej części transakcji, musi zarezerwować wolne miejsce w dzienniku transakcji, aby umożliwić wycofanie rekordu dziennika bez konieczności powiększania dziennika transakcji.
Mechanizm rezerwacji miejsca w logu jest bardzo konserwatywny, zawsze rezerwuje wystarczającą ilość miejsca, a zwykle więcej, na wypadek nieoczekiwanej sytuacji. Na przykład aktualizacja lub usunięcie w tabeli skompresowanej zarezerwuje więcej miejsca niż podobna aktualizacja lub usunięcie w tabeli nieskompresowanej. Dzieje się tak, ponieważ wycofanie aktualizacji w skompresowanej tabeli może wymagać radzenia sobie z tym, że zaktualizowany wiersz nie znajduje się już na skompresowanej stronie, a zatem musiałby zapisywać kolumny o pełnej szerokości w rekordzie dziennika zamiast skompresowanych danych.
Typy rekordów dziennika
Istnieje wiele typów zapisów dziennika, w tym:
- LOP_FORMAT_PAGE Operacja strony formatu dziennika — to miejsce, w którym strona została sformatowana, co oznacza, że utworzono jej nagłówek. Rekord dziennika rejestruje przynajmniej nagłówek strony i potencjalnie trochę więcej zawartości strony, jeśli strona została utworzona i wypełniona w ramach operacji, takiej jak budowanie lub przebudowa indeksu)
- LOP_MODIFY_ROW Ta operacja zmienia niewielką część istniejących danych.
- LOP_SET_BITS Ten rekord dziennika dotyczy bitmap alokacji.
- LOP_INSERT_ROWS i LOP_DELETE_ROWS
- LOP_SET_FREE_SPACE Dotyczy PFS – alokacja mapy bitowej, która śledzi stany alokacji stron.
Wszelkie rekordy dziennika, które będą wprowadzać zmiany na stronie danych lub stronie indeksu w indeksie tabelarycznym, obejmują :
- Identyfikator jednostki alokacji
- Identyfikator strony i identyfikator boksu rekordu na stronie, który zasadniczo jest liczonym od zera identyfikatorem rekordu danych lub rekordu indeksu na stronie.
- Powidok lub obraz przed i po zmienionych danych. W jednym rekordzie dziennika może znajdować się wiele ich zestawów. Powidoki pozwalają na ponowne wykonanie. Obrazy sprzed pozwalają na cofnięcie się.
Zablokuj rejestrowanie
Niektóre rekordy dziennika zawierają bitmapę, której blokady były utrzymywane w momencie, gdy miała miejsce opisana zmiana. Mapa bitowa zawiera:
- Liczba zamków.
- Jaki typ i tryb blokady – na przykład blokada strony w trybie X.
- Co jest na zamku
Podczas przywracania po awarii i przełączania awaryjnego na dublowanie bazy danych/grupę dostępności te blokady zostaną nabyte dla wszystkich rekordów dziennika, które mają zostać cofnięte. Pozwala to na szybkie odzyskiwanie w wersji Enterprise Edition począwszy od SQL Server 2005.
Rekordy dziennika w transakcjach
Wszystkie transakcje generują co najmniej trzy rekordy dziennika, zawsze w następującej kolejności:
- LOP_BEGIN_XACT – zawiera informacje takie jak SPID, nazwa transakcji i czas rozpoczęcia. Wszystkie transakcje uruchamiane przez SQL Server mają nazwy opisujące operację (np. AllocFirstPage, DROPOBJ)
- Inne rekordy dotyczące transakcji.
- LOP_COMMIT_XACT – jeśli transakcja zostanie zatwierdzona.
- LOP_ABORT_XACT – jeśli transakcja się cofnie.
Obydwa obejmują czas zakończenia transakcji.
Rekordy dziennika w transakcji są połączone wstecznie przez LSN. Oznacza to, że następny rekord dziennika wygenerowany dla transakcji ma numer LSN poprzedniego rekordu dziennika, który został wygenerowany dla tej konkretnej transakcji. Pozwala to na prawidłowe wycofanie transakcji.Niektóre wpisy w dzienniku są w ogóle nietransakcyjne, w tym:
- Zmiany wolnej przestrzeni PFS (niemożliwe do uzgodnienia z innymi transakcjami)
- Różnicowe zmiany bitmapy (tylko zmiana w jedną stronę)
Badanie zapisów dziennika
Istnieją dwa sposoby sprawdzania zapisów dziennika. Możesz użyć funkcji DBCC LOGINFO, ale zaleca się użycie funkcji zwracającej tabelę fn_dblog. Ma niezwykle prostą składnię:
SELECT * FROM fn_dblog (startLSN, endLSN); GO
Jest to niezwykle potężna funkcja, ponieważ:
- zwraca tabelaryczny zestaw wyników, którym można łatwo zarządzać.
- pozwala na użycie złożonych predykatów.
- skanuje cały dziennik transakcji w aktywnej części dziennika, od początku najstarszej niezatwierdzonej transakcji do najnowszego rekordu dziennika. Można to zmienić za pomocą flagi śledzenia 2537
Pola startLSN i endLSN są zwykle przekazywane jako NULL
Oto demo:
USE DBTest2014 GO SET NOCOUNT ON; GO --Set the SIMPLE recovery mode with no auto-stats -- to avoid unwanted log records ALTER DATABASE DBTest2014 SET RECOVERY SIMPLE; ALTER DATABASE DBTest2014 SET AUTO_CREATE_STATISTICS OFF; CREATE TABLE [TEST_TABLE] ([C1] INT, [C2] INT, [C3] INT); INSERT INTO [TEST_TABLE] VALUES (1,1,1); GO --Clear out the log CHECKPOINT; GO -- Implicit transaction INSERT INTO [TEST_TABLE] VALUES (2,2,2); GO SELECT * FROM fn_dblog(null, null); GO
Oto skrócony zestaw wyników. W rzeczywistości fn_dblog zwraca wiele różnych rekordów, takich jak Długość rekordu dziennika, Bity flagi, Rezerwa dziennika, AllocUnitId, PageID, SlotID, Poprzednia strona LSN i inne.
Modyfikowanie zawartości wiersza
Modyfikacje dziennika są rejestrowane na dwa sposoby:jako LOP_MODIFY_ROW lub LOP_MODIFY_COLUMNS nagrywać. Bez względu na to, która metoda zostanie użyta, będzie rejestrować bajty, które są faktycznie zmieniane. Na przykład zmiana wartości INT z 1 na 24 rejestruje tylko jeden bajt zmiany, ponieważ pozostałe trzy bajty zerowe nie uległy zmianie. SQL Server użyje LOP_MODIFY_ROW rekord dziennika, jeśli jest aktualizowana pojedyncza część wiersza. Część definiuje się w następujący sposób:każda kolumna w wierszu o zmiennej długości jest „porcją”, a cały obszar wiersza o stałej szerokości jest „porcją”, nawet jeśli aktualizowanych jest wiele kolumn, ale tylko wtedy, gdy bajty są aktualizowane są w odstępie 16 bajtów lub mniej w rzędzie.
LOP_MODIFY_ROW zawiera:
- Przed obrazem
- Po obrazie
- Kolumny klucza indeksu, jeśli dotyczy
- Zablokuj mapę bitową
LOP_MODIFY_COLUMNS zawiera:
- Tablica przesunięć przed i po
- Tablica długości
- Kolumny klucza indeksu, jeśli dotyczy
- Zablokuj mapę bitową
- Pary obrazów przed i po
Zapisy dziennika wynagrodzeń
Jest to specjalny rodzaj rekordów dziennika, które są używane do pomocy przy wycofywaniu transakcji. Po wycofaniu transakcji zmiana opisana przez każdy rekord dziennika w transakcji musi zostać cofnięta w bazie danych. Wycofanie rozpoczyna się od najnowszego rekordu dziennika transakcji i podąża za poprzednimi łączami LSN, aż do rekordu dziennika LOP_BEGIN_XACT. Dla każdego wpisu dziennika:
- Wykonaj „antyoperację”, która neguje skutki zapisu dziennika
- Wygeneruj rekord dziennika, oznaczając go jako rekord dziennika KOMPENSACJI, ponieważ kompensuje on rekord dziennika w przedniej części transakcji.
- Poprzednia LSN rekordu dziennika COMPENSATION wskazuje na rekord dziennika przed tym, za który jest kompensowany. Zasadniczo powoduje to, że rekord dziennika nie jest już częścią łańcucha rekordów dziennika dla transakcji.
- Zarezerwowane miejsce w dzienniku dla rekordu dziennika zostało zwolnione
Rekordów dziennika KOMPENSACJI nie można cofnąć, można je tylko powtórzyć.
Wycofanie transakcji
Oto graficzna reprezentacja tego, co się dzieje, gdy transakcja się cofa:
Zbadajmy następujący kod:
USE DBTest2014 GO SET NOCOUNT ON; GO ALTER DATABASE DBTest2014 SET RECOVERY SIMPLE; ALTER DATABASE DBTest2014 SET AUTO_CREATE_STATISTICS OFF; CREATE TABLE [TEST_TABLE] ([C1] INT, [C2] INT, [C3] INT); INSERT INTO [TEST_TABLE] VALUES (1,1,1); INSERT INTO [TEST_TABLE] VALUES (2,2,2); GO --Clear out the log CHECKPOINT; GO -- Explicit transaction to insert a new record BEGIN TRAN; INSERT INTO [TEST_TABLE] VALUES (3,3,3); GO SELECT * FROM fn_dblog(null, null); GO --Roll it back ROLLBACK TRAN; GO SELECT * FROM fn_dblog(null, null);
Tutaj widzimy specjalny zapis logu z opisem „KOMPENSATY”
Jeśli spojrzymy na poprzednią LSN, zobaczymy, że LOP_INSERT_ROWS że zrobiliśmy, linki z powrotem do …0f40:0001 i jest to transakcja BEGIN , ponieważ przekazywana część transakcji łączy się z poprzednim rekordem dziennika. LOP_DELETE_ROW rekord dziennika wynagrodzeń nie odsyła do rekordu, za który kompensuje — odsyła do niego (do rekordu dziennika transakcji BEGIN).
Tak więc DELEDE skompensował INSERT i usunął go z listy rekordów dziennika. LOP_ABORT_XACT jest sygnałem, że transakcja została zakończona wycofaniem. Możesz także zobaczyć, że LOP_ABORT_XACT linki z powrotem do LOP_BEGIN_XACT.
Kiedy robimy zapis dziennika wynagrodzeń, rezerwacja miejsca na log spada [-74]. Czyli faktycznie oddaje trochę miejsca, które było zarezerwowane dla części forwardowej transakcji (LOP_INSERT_ROWS [178]). Jak widać, system rezerwacji miejsca w logu jest bardzo konserwatywny — INSERT rezerwuje więcej miejsca niż DELETE oddaje.
Wycofywanie zmian i różnicowe kopie zapasowe
Jeśli baza danych ma pełną kopię zapasową, to transakcja aktualizuje 100 000 rekordów, ale transakcja jest wycofywana, dlaczego różnicowa kopia zapasowa zawiera tak dużo danych? Na pewno cofnięcie transakcji oznacza, że nic się nie zmieniło? Brakujący element układanki polega na tym, że wycofanie transakcji nie wymazuje wszystkich zmian wprowadzonych przez transakcję. Jak widzieliśmy, wycofanie musi generować rekordy dziennika kompensacji, ponieważ wycofanie musi generować inne zmiany, aby skompensować przeszłą część transakcji. Nagłówki wszystkich stron, których dotyczy problem, zostały zmienione co najmniej dwukrotnie. Jeden, aby zaktualizować numer LSN strony dla części przekazującej transakcję i raz, aby zaktualizować numer LSN strony dla części wycofania transakcji. W obu przypadkach aktualizacja spowoduje oznaczenie zakresu jako zmienionego w różnicowej mapie bitowej. Nie obchodzi go, jaka była zmiana, tylko, że coś się w pewnym stopniu zmieniło. Nie ma możliwości wykluczenia tych zakresów z różnicowej kopii zapasowej.
Podsumowanie
W tym artykule przyjrzeliśmy się zapisom dziennika. Rekordy dziennika są podstawą mechanizmów rejestrowania i odzyskiwania. Z każdą zmianą w bazie danych powiązany jest rekord dziennika. Każdy zapis dziennika opisuje małą zmianę. Duża zmiana ma wiele rekordów dziennika w jednej transakcji. Istnieje wiele różnych typów zapisów dziennika i przyjrzeliśmy się kilku z nich.
Dziennik transakcji to w gruncie rzeczy ogromny temat i kilka artykułów nie wystarczy, aby odsłonić wszystkie szczegóły. Jeśli więc chcesz uzyskać bardziej szczegółowe informacje, proponuję przeczytać następującą książkę:Zarządzanie dziennikiem transakcji SQL Server autorstwa Tony'ego Davisa i Gail Shaw i ten artykuł:Zarządzanie dziennikiem transakcji.
Przeczytaj także:
Zanurz się w dziennik transakcji SQL Server — część 1
Zagłęb się w dziennik transakcji SQL Server — część 2