Log transakcyjny w SQL Server 2005, cz. I

Opublikowano: 15 listopada 2007
Zawartość strony
WstępWstęp
Dziennik transakcji i proces modyfikacji danych w SQL Server 2005Dziennik transakcji i proces modyfikacji danych w SQL Server 2005
Logiczna budowa dziennika transakcjiLogiczna budowa dziennika transakcji
Fizyczna budowa dziennika transakcjiFizyczna budowa dziennika transakcji
Przeczytaj pozostałe części tego artykułuPrzeczytaj pozostałe części tego artykułu

Wstęp

Log transakcyjny (zwany także dziennikiem transakcji) jest bez wątpienia jednym z kluczowych komponentów każdej bazy danych. Wpływa bezpośrednio na szereg aspektów związanych z jej funkcjonowaniem – od fizycznego rozmiaru plików na nośniku dyskowym, poprzez sposób działania mechanizmów odzyskiwania (ang. database restore), po wydajność poszczególnych operacji DML i DDL.

Log transakcyjny jest również gwarantem podstawowych i niezbędnych właściwości transakcji określanych akronimem ACID (z ang. - Atomicity, Consistency, Isolation, Durability, czyli odpowiednio: atomowość, spójność, izolacja, trwałość). Microsoft SQL Server 2005 jest jednym z tych silników bazodanowych, dla których dziennik transakcji ma istotnie zasadnicze znaczenie. W niniejszym artykule postaram się zaprezentować m.in. działanie logu transakcyjnego oraz jego budowę, jak też omówić najlepsze praktyki związane z jego obsługą.

Do początku stronyDo początku strony

Dziennik transakcji i proces modyfikacji danych w SQL Server 2005

Każda baza danych SQL Server posiada własny dziennik transakcji, który rejestruje wszystkie transakcje oraz zmiany danych i struktury bazy przez nie dokonane. Dziennik zapewnia obsługę zatwierdzania i wycofywania transakcji, finalizowania niekompletnych (niezatwierdzonych i niewycofanych) transakcji w momencie startu serwera oraz odtwarzania stanu bazy danych do określonego momentu (najczęściej sprzed awarii). Umożliwia również działanie takich funkcjonalności, jak replikacja transakcyjna, czy database mirroring.

Log transakcyjny jest zaimplementowany jako pojedynczy plik lub zestaw plików, posiada także dedykowaną przestrzeń w cache’u – tzw. log cache, który funkcjonuje oddzielnie w stosunku do buffer cache’u. Strona loga (ang. log page) jest zbudowana zasadniczo inaczej niż strona danych (ang. data page). Warto pamiętać, że dziennik transakcji w SQL Server 2005 jest zaimplementowany w technologii WAL (ang. Write-Ahead Log). Model ten gwarantuje, że żadna modyfikacja strony danych nie zostanie zapisana na dysku zanim na dysk nie trafi strona logu odnosząca się do tej modyfikacji.

Zrozumienie zasady działania dziennika transakcji nie jest możliwe bez uświadomienia sobie jego roli w procesie zapisu zmodyfikowanych danych na stabilnym nośniku fizycznym (ang. stable media). Proces ten został zilustrowany na Rysunku 1. W sytuacji, w której SQL Server otrzyma żądanie modyfikacji określonego rekordu, strona danych, na której znajduje się ten rekord zostaje skopiowana do buffer cache’u, o ile oczywiście jej kopia nie znajdowała się tam wcześniej. Następnie strona ta zostaje w buffer cache’u zmodyfikowana zgodnie z żądaniem. W tym samym momencie tworzony jest rekord logu, który reprezentuje żądaną modyfikację danych. Rekord ten egzystuje w log cache’u na jednej ze stron logu – podobnie więc jak w przypadku strony danych, opisywane operacje odbywają się w buforze – więc w pamięci RAM, a nie na dysku. Zmodyfikowana strona danych, która nie została jeszcze zapisana na dysk, określana jest mianem brudnej strony (ang. dirty page). Brudne strony danych mogą trafić na dysk pod bezwzględnym warunkiem, wynikającym z założeń WAL, że powiązane z nimi log pages zostały wcześniej zapisane na stabilnym nośniku. Rekordy logu są natomiast zapisywane na dysk w sytuacji zatwierdzania transakcji (czyli podczas jawnej lub niejawnej operacji COMMIT TRAN). Dopiero w tym momencie brudna strona danych może zostać wysłana na dysk, co określa się mianem page flushing.

Page flushing ma miejsce w dwóch sytuacjach. Po pierwsze, wszystkie brudne strony danych trafiają na dysk w momencie wystąpienia operacji checkpoint (tzw. punkt kontrolny). SQL Server przeprowadza checkpoint okresowo (interwał jest ustalany dynamicznie i nie mamy na niego bezpośredniego wpływu), po wystąpieniu określonego zdarzenia (m.in. zatrzymanie instancji, backup bazy danych, dodanie nowego pliku MDF) lub na żądanie (komenda CHECKPOINT wydana przez użytkownika). Po drugie, niektóre brudne strony danych trafiają na dysk w sytuacji, w której cache, będąc przecież przestrzennie ograniczony wielkością pamięci RAM, musi zostać częściowo zwolniony, aby udostępnić miejsce na nowe strony danych.

Rys. 1. Proces modyfikacji danych w SQL Server 2005.

Rys. 1. Proces modyfikacji danych w SQL Server 2005.

Trzeba pamiętać, że od chwili zatwierdzenie transakcji, w której zmodyfikowano określoną stronę danych (strzałka nr 3 na Rysunku 1), do chwili odwzorowania tej modyfikacji na stabilnym nośniku (strzałka nr 4 na Rysunku 1) – może upłynąć względnie długi czas. Mogłoby się wydawać, że jeżeli w okresie istnienia brudnych stron danych nastąpi awaria serwera, to niektóre modyfikacje będą zagubione. WAL gwarantuje jednak, że tak się nie dzieje. SQL Server po odzyskaniu sprawności będzie w stanie odtworzyć wszystkie modyfikacje właśnie dzięki temu, że zostały one trwale zarejestrowane w dzienniku transakcji.

Do początku stronyDo początku strony

Logiczna budowa dziennika transakcji

Log transakcyjny jest uporządkowanym zbiorem wpisów (rekordów), zawierających informacje o transakcjach i zmianach danych. Każdy rekord dziennika transakcji identyfikowany jest przez LSN (Log Sequence Number). Zawiera także identyfikator transakcji (Transaction ID), do której przynależy, jak również pole typu varbinary, w którym zakodowana jest informacja o modyfikacji. Informacja ta w rzeczywistości nie jest pełnym odwzorowaniem wartości przed zmianą oraz wartości po zmianie – przypomina raczej różnicę bitową, gdyż jest to najwydajniejszy sposób przechowywania informacji o zmianach. Nowe rekordy dziennika dodawane są zawsze na końcu dziennika z kolejnym numerem LSN. Dla poprawienia wydajności operacji wycofywania transakcji, każdy rekord oznaczony jest wskaźnikiem do poprzedniego numeru LSN rekordu tej samej transakcji (Previous LSN).

W logu transakcyjnym zapisywane są informacje bardzo różnego rodzaju. Oprócz wszelkiego typu modyfikacji (powodowanych przez operacje DML i DDL), dokładnie logowane są także: początek i koniec transakcji, każda alokacja i dealokacja stron i extentów, jak też m.in. operacje wycofania transakcji, checkpoint, shrinkpoint. Jeżeli chodzi o logowanie wycofywania transakcji, to warto dodać, że jednym z warunków umożliwiających rozpoczęcie każdej transakcji jest zarezerwowanie w logu dostatecznej przestrzeni nie tylko na skuteczne zalogowanie całej transakcji, ale także na jej wycofanie. Upraszczając, jeżeli transakcja T wymaga zaalokowania X stron dziennika, to SQL Server zaalokuje dla tej transakcji przestrzeń logu równą w przybliżeniu 2X, aby zagwarantować skuteczne przeprowadzenie operacji wycofania transakcji na każdym jej etapie.

W dzienniku transakcji wyróżnia się tzw. aktywną część dziennika, czyli obszar logu od najwcześniejszego rekordu należącego do aktywnej (otwartej – niezatwierdzonej lub nawet zatwierdzonej, ale niezreplikowanej) transakcji do ostatniego rekordu dziennika. Aktywna część dziennika transakcji nie może zostać obcięta. Częstym błędem jest twierdzenie, że w aktywnej części dziennika nie mogą znajdować się zatwierdzone transakcje. Oczywiście mogą się tam znajdować, a ma to miejsce choćby w sytuacji, w której transakcja wcześniej rozpoczęta nie została jeszcze zatwierdzona, a transakcja rozpoczęta później – tak.

Dostęp do dziennika transakcji jest możliwy tylko na zasadzie odczytu przez nieudokumentowane funkcje. Co ciekawe, interfejs umożliwia wykonanie próby polecenia UPDATE na dzienniku transakcji, kończy się to jednak krytycznym wyjątkiem SQL Server, co nie powinno nikogo dziwić. Na Rysunku 2 zaprezentowany jest przykładowa zawartość dziennika transakcji odczytana przy pomocy nieudokumentowanej funkcji fn_dblog().

Rys. 2. Przykładowa zawartość dziennika transakcji.

Rys. 2. Przykładowa zawartość dziennika transakcji.

Przykładowa zawartość dziennika z Rysunku 2 została wygenerowana za pomocą poniższego skryptu Transact-SQL:

CREATE DATABASE tran_log
GO
USE tran_log; 
GO
CREATE TABLE tabA (colA int)
GO
INSERT INTO tabA (colA) SELECT 1; 
GO
SELECT * FROM ::fn_dblog(NULL, NULL); 
GO
-- USE master;
-- DROP DATABASE tran_log

Funkcja fn_dblog() przyjmuje dwa parametry – początkowy i końcowy LSN żądanego zbioru wynikowego. Jeżeli podane zostaną wartości NULL, wynik nie będzie zawężony i odczytany zostanie cały log bieżącej bazy danych.

Do początku stronyDo początku strony

Fizyczna budowa dziennika transakcji

Log transakcyjny zaimplementowany jest jako zbiór plików LDF (Log Data File). Ilość plików logu wynika z konfiguracji bazy (Database Properties -> Files w SQL Server Management Studio). W praktyce często stosuje się pojedynczy plik, gdyż z istnienia kilku plików nie ma praktycznie żadnego pożytku (silnik i tak nie będzie używał ich równolegle, lecz sekwencyjnie). SQL Server dzieli każdy plik logu na określoną liczbę tzw. wirtualnych plików logu – VLF (Virtual Log Files). Zarówno wielkość wirtualnego pliku logu, jak i ich ilość, jest dobierana przez silnik bazodanowy dynamicznie w momencie tworzenia lub powiększania fizycznego pliku dziennika.

Silnik bazodanowy SQL Server 2005 stara się utrzymać jak najmniejszą liczbę VLFów. Ich duża liczba może powodować pogorszenie parametrów wydajnościowych większości operacji wykonywanych na dzienniku transakcji. W praktyce wzrost liczby VLFów wiąże się najczęściej z automatycznym powiększaniem rozmiaru pliku dziennika. Dlatego ważne jest, aby starać się dobrać taką wielkość pliku dziennika, która odpowiada docelowym potrzebom. Najgorszym przypadkiem jest sytuacja, w której plik logu był wielokrotnie zwiększany o niewielkie ilości przestrzeni dyskowej (mała wartość growth_increment opcji FILEGROWTH) – wtedy liczba VLFów będzie duża i prawdopodobnie zacznie w zauważalny sposób wpływać na wydajność logu, zwłaszcza podczas operacji tworzenia kopii zapasowych. Liczba VLFów powstałych podczas automatycznego lub manualnego zwiększania rozmiaru pliku dziennika jest ściśle określona i uzależniona od wielkości dodawanej części. Zależność ta zaprezentowana jest w Tabeli 1.

Tabela 1. Liczba powstałych VLFów w zależności od rozmiaru dodanej przestrzeni logu
Dodana przestrzeń loguIlość powstałych VLFów

poniżej 64 MB

4

65 MB – 1 GB

8

powyżej 1 GB

16

Plik dziennik transakcji jest w specyficzny sposób zapełniany kolejnymi danymi w kierunku końca pliku, co ilustruje Rysunek 3. Operacja obcięcia logu (TRUNCATE) uwalnia te wirtualne pliki VLF (zawsze w całości), których wszystkie rekordy znajdują się przed punktem minimum recovery LSN (MinLSN na Rysunku 3). Punkt ten wyznaczony jest przez początek aktywnej części logu, czyli przez LSN rekordu, którego obecność jest wymagana do skutecznego przeprowadzenia operacji wycofania najstarszej aktywnej transakcji. Trzeba też pamiętać, że jeżeli dana baza oznaczona jest jako publisher w replikacji transakcyjnej, to mimo zatwierdzenia transakcji – nie będzie ona uznana za zakończoną (nieaktywną), dopóki log reader nie przeniesie jej do bazy distribution.

Rys. 3. Struktura wewnętrzna pliku dziennika transakcji.

Rys. 3. Struktura wewnętrzna pliku dziennika transakcji.

W momencie, w którym aktywna część logu osiąga koniec pliku dziennika (koniec ostatniego pliku VLF), następuje kontynuacja zapisu nowych rekordów logu od początku pliku, o ile oczywiście znajduje się tam wolna (obcięta) przestrzeń. Prezentuje to Rysunek 4.

Rys. 4. Ponowny zapis od początku pliku dziennika.

Rys. 4. Ponowny zapis od początku pliku dziennika.

Oczywiście nie w każdym przypadku na początku pliku dziennika znajdzie się dostępna przestrzeń i może nastąpić sytuacja, w której aktywna część logu dotrze do najstarszego nie obciętego rekordu logu (Start of logical log na Rysunkach 3 i 4). W takiej sytuacji, o ile włączona jest opcja FILEGROWTH, nastąpi zaalokowanie dodatkowej, określonej przez wartość growth_increment, przestrzeni dyskowej na fizyczny plik dziennika (nowe strony logu dodawane będą na końcu zwiększonego pliku). Jeżeli opcja FILEGROWTH będzie natomiast wyłączona, SQL Server wygeneruje błąd 9002 i natychmiast wycofa bieżącą transakcję:

Msg 9002, Level 17, State 4, Line 1
The transaction log for database 'some_database' is full. To find out why space in the log 
cannot be reused, see the log_reuse_wait_desc column in sys.databases

Odpowiednio częste przycinanie logu gwarantuje, że nie nastąpi ani zwiększenie rozmiaru pliku LDF, ani nie dojdzie do wymienionego wyżej błędu. Jeżeli określona baza danych dysponuje więcej niż jednym plikiem LDF, cykl zapełniania dziennika odbywa się po kolei we wszystkich plikach, zanim ponownie zapisywany zacznie być pierwszy z nich.

CDN...

Do początku stronyDo początku strony

Przeczytaj pozostałe części tego artykułu

Log transakcyjny w SQL Server 2005, cz. II


Marcin Guzowski

Marcin Guzowski
Zawodowo zajmuje się inżynierią oprogramowania, w szczególności rozwiązaniami data-centric. Obecnie jest szefem zespołu pracującego nad usługami inteligentnej automatyzacji danych (m.in. deduplikacji, standaryzacji, kojarzenia danych podobnych) w Dziale Badawczo-Rozwojowym w firmie Anica System S.A. Praktycznie od początku swojej przygody z bazami danych intensywnie wykorzystuje SQL Server jako podstawową platformę bazodanową, także do zastosowań stosunkowo nietypowych i wymagających maksymalizacji wydajności działania.


Do początku stronyDo początku strony