W świecie SQL Server istnieje uporczywy mit, że zarówno DROP TABLE
i TRUNCATE TABLE
polecenia nie są rejestrowane.
Oni nie są. Oba są w pełni rejestrowane, ale skutecznie rejestrowane.
Możesz łatwo to sobie udowodnić. Uruchom następujący kod, aby skonfigurować testową bazę danych i tabelę, i pokaż, że w naszej tabeli mamy 10 000 wierszy:
CREATE DATABASE [TruncateTest]; GO ALTER DATABASE [TruncateTest] SET RECOVERY SIMPLE; GO USE [TruncateTest]; GO CREATE TABLE [TestTable] ( [c1] INT IDENTITY, [c2] CHAR (8000) DEFAULT 'a'); GO SET NOCOUNT ON; GO INSERT INTO [TestTable] DEFAULT VALUES; GO 10000 SELECT COUNT (*) AS N'RowCount' FROM [TestTable]; GO
Wyniki:
Liczba wierszy———–
10000
A potem następujący kod, który obcina tabelę w transakcji i sprawdza liczbę wierszy:
BEGIN TRAN; GO TRUNCATE TABLE [TestTable]; GO SELECT COUNT (*) AS N'RowCount' FROM [TestTable]; GO
Wyniki:
Liczba wierszy———–
0
Teraz stół jest pusty. Ale mogę wycofać transakcję i przywrócić wszystkie dane:
ROLLBACK TRAN; GO SELECT COUNT (*) AS N'RowCount' FROM [TestTable]; GO
Wyniki:
Liczba wierszy———–
10000
Wyraźnie TRUNCATE
operacja musi być zarejestrowana, w przeciwnym razie operacja wycofania nie zadziała.
Skąd więc bierze się błędne przekonanie?
Pochodzi z zachowania DROP
i TRUNCATE
operacje na dużych stołach. Zakończą się niemal natychmiast, a jeśli zajrzysz do dziennika transakcji za pomocą fn_dblog
zaraz potem zobaczysz tylko niewielką liczbę rekordów dziennika wygenerowanych z operacji. Ta mała liczba nie koreluje z rozmiarem obcinanej lub usuwanej tabeli, więc wygląda na to, że DROP
i TRUNCATE
operacje nie są rejestrowane.
Ale są w pełni zalogowane, jak pokazałem powyżej. Więc gdzie są zapisy dziennika dotyczące operacji?
Odpowiedź jest taka, że rekordy dziennika zostaną utworzone, ale nie natychmiast, przez mechanizm zwany „odroczonym upuszczaniem”, który został dodany w dodatku SQL Server 2000 SP3.
Gdy tabela zostanie usunięta lub obcięta, wszystkie strony pliku danych przydzielone do tabeli muszą zostać cofnięte. Mechanizm tego działania przed SQL Server 2000 SP3 był następujący:
Dla każdego ekstentu przydzielonego do tabeliRozpocznij
Uzyskaj wyłączną blokadę alokacji dla ekstentu
Sonda blokada strony dla każdej strony w zakresie (uzyskaj blokadę w trybie wyłączności i natychmiast ją usuń, upewniając się, że nikt inny nie ma zablokowanej strony)
NIE zwolnić blokadę zasięgu, gwarantującą, że nikt inny nie będzie mógł jej użyć
Przejdź do następnego zakresu
Koniec
Ponieważ wszystkie blokady zakresu były utrzymywane do końca operacji, a każda blokada zajmuje niewielką ilość pamięci, możliwe było, że menedżer blokad zabraknie pamięci, gdy DROP
lub TRUNCATE
bardzo dużego stołu. Niektórzy klienci korzystający z SQL Server zaczęli odkrywać, że napotykają problemy z brakiem pamięci w SQL Server 2000, ponieważ tabele rozrastały się do bardzo dużych rozmiarów i znacznie przewyższały wzrost pamięci systemowej.
Mechanizm odroczonego upuszczania symuluje DROP
lub TRUNCATE
operacja zostaje zakończona natychmiast, poprzez odczepienie alokacji dla tabeli i umieszczenie ich w „kolejce z odroczonym upuszczaniem”, w celu późniejszego przetworzenia przez zadanie w tle. Ta operacja odczepiania i przesyłania generuje tylko kilka rekordów dziennika. Jest to operacja, która jest wykonywana i wycofywana w powyższym przykładzie kodu.
„Zadanie odroczonego opuszczania w tle” obraca się co kilka sekund i cofa wszystkie strony i ekstenty w kolejce odroczonego opuszczania w małym wsady, gwarantując, że operacji nie zabraknie pamięci. Wszystkie te dezalokacje są w pełni rejestrowane, ale pamiętaj, że cofnięcie alokacji strony pełnej rekordów danych lub indeksu nie powoduje rejestrowania pojedynczych usunięć tych rekordów; zamiast tego cała strona jest po prostu oznaczona jako cofnięta alokacja w odpowiedniej mapie bajtowej alokacji PFS (Page Free Space).
Począwszy od SQL Server 2000 SP3, po wykonaniu DROP
lub TRUNCATE
tabeli, zobaczysz tylko kilka generowanych rekordów dziennika. Jeśli poczekasz około minuty, a następnie ponownie zajrzysz do dziennika transakcji, zobaczysz tysiące rekordów dziennika wygenerowanych przez operację odroczonego upuszczania, z których każdy zwalnia stronę lub zakres. Operacja jest w pełni i wydajnie rejestrowana.
Oto przykład, wykorzystując scenariusz, który stworzyliśmy powyżej:
CHECKPOINT; GO TRUNCATE TABLE [TestTable]; GO SELECT COUNT (*) AS N'LogRecCount' FROM fn_dblog (NULL, NULL); GO
Wyniki:
LogRecCount———–
25
Jak widać, wyraźnie nie ma rekordów logów zwalniających alokację 10 000 stron plus 1250 ekstentów w tabeli TestTable.
Jeśli poczekam kilka sekund, a następnie uruchomię fn_dblog
kod ponownie, otrzymuję:
———–
3811
Możesz się zastanawiać, dlaczego nie ma co najmniej 10 000 rekordów dziennika – po jednym dla każdej strony, która jest zwalniana. Dzieje się tak, ponieważ cofanie alokacji stron jest nawet wydajnie rejestrowane – z jednym rekordem dziennika odzwierciedlającym zmiany alokacji stron PFS dla 8 kolejnych stron pliku danych, zamiast jednego rekordu dziennika dla każdej strony pliku danych, odzwierciedlającego zmianę statusu alokacji na stronie PFS.
SQL Server zawsze stara się generować jak najmniej dzienników transakcji, przy jednoczesnym przestrzeganiu zasad dotyczących pełnego lub minimalnego rejestrowania w oparciu o bieżący model odzyskiwania. Jeśli chcesz przejrzeć rzeczywiste rekordy dziennika generowane przez mechanizmy unhook-and-transfer i deferred-drop, po prostu zamień * na COUNT (*) w powyższym kodzie fn_dblog i poszukaj transakcji z nazwą transakcji ustawioną na DeferredAllocUnitDrop::Process
.
W przyszłych postach omówię elementy wewnętrzne, które leżą u podstaw innych uporczywych mitów dotyczących aspektów wydajności SQL Server Storage Engine.