Er is een hardnekkige mythe in de SQL Server-wereld dat zowel de DROP TABLE
en TRUNCATE TABLE
commando's zijn niet gelogd.
Zij zijn niet. Ze zijn allebei volledig gelogd, maar efficiënt gelogd.
Dit kun je jezelf gemakkelijk bewijzen. Voer de volgende code uit om een testdatabase en -tabel in te stellen en laat zien dat we 10.000 rijen in onze tabel hebben:
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
Resultaten:
RowCount———–
10000
En dan de volgende code, die de tabel in een transactie afkapt en het aantal rijen controleert:
BEGIN TRAN; GO TRUNCATE TABLE [TestTable]; GO SELECT COUNT (*) AS N'RowCount' FROM [TestTable]; GO
Resultaten:
RowCount———–
0
Nu is de tafel leeg. Maar ik kan de transactie terugdraaien en alle gegevens weer terugzetten:
ROLLBACK TRAN; GO SELECT COUNT (*) AS N'RowCount' FROM [TestTable]; GO
Resultaten:
RowCount———–
10000
Duidelijk de TRUNCATE
bewerking moet worden vastgelegd, anders zou de terugdraaibewerking niet werken.
Dus waar komt de misvatting vandaan?
Het komt van het gedrag van DROP
en TRUNCATE
bewerkingen op grote tafels. Ze worden bijna onmiddellijk voltooid en als u in het transactielogboek kijkt met fn_dblog
direct daarna ziet u slechts een klein aantal logboekrecords die door de bewerking zijn gegenereerd. Dat kleine aantal correleert niet met de grootte van de tabel die wordt afgekapt of verwijderd, dus het lijkt alsof DROP
en TRUNCATE
bewerkingen worden niet gelogd.
Maar ze zijn volledig geregistreerd, zoals ik hierboven heb aangetoond. Dus waar zijn de logrecords voor de operaties?
Het antwoord is dat de logboekrecords worden gemaakt, alleen niet onmiddellijk, door een mechanisme genaamd 'uitgestelde drop', dat is toegevoegd in SQL Server 2000 SP3.
Wanneer een tabel wordt verwijderd of afgekapt, moeten alle gegevensbestandspagina's die aan de tabel zijn toegewezen, worden verwijderd. Het mechanisme hiervoor vóór SQL Server 2000 SP3 was als volgt:
Voor elke omvang die aan de tafel is toegewezenBegin
Verkrijg een exclusieve toewijzingsvergrendeling op de omvang
Onderzoek de paginavergrendeling voor elke pagina in de mate (verkrijg de vergrendeling in de exclusieve modus en laat deze onmiddellijk vallen, zorg ervoor dat niemand anders de pagina vergrendeld heeft)
NIET ontgrendel de extensie, zodat niemand anders die extensie kan gebruiken
Ga naar de volgende extensie
Einde
Aangezien alle vergrendelingen werden vastgehouden tot het einde van de operatie, en elke vergrendeling een kleine hoeveelheid geheugen in beslag neemt, was het mogelijk dat de vergrendelingsbeheerder onvoldoende geheugen had wanneer een DROP
of TRUNCATE
van een zeer grote tafel plaatsvond. Sommige SQL Server-klanten begonnen te ontdekken dat ze onvoldoende geheugen hadden op SQL Server 2000, omdat tabellen erg groot werden en de groei van het systeemgeheugen enorm overtroffen.
Het uitgestelde-dropmechanisme simuleert de DROP
of TRUNCATE
bewerking onmiddellijk voltooien, door de toewijzingen voor de tafel los te koppelen en in de 'uitgestelde-drop-wachtrij' te plaatsen, voor latere verwerking door een achtergrondtaak. Deze onthaak-en-overdrachtsbewerking genereert slechts een handvol logrecords. Dit is de bewerking die wordt uitgevoerd en teruggedraaid in mijn codevoorbeeld hierboven.
De 'deferred-drop background task' start om de paar seconden op en heft alle pagina's en delen in de uitgestelde-drop-wachtrij op in kleine batches, zodat de bewerking niet zonder geheugen komt te zitten. Deze deallocaties zijn allemaal volledig gelogd, maar onthoud dat het vrijgeven van een pagina vol gegevens of indexrecords geen individuele verwijderingen van die records registreert; in plaats daarvan wordt de hele pagina gemarkeerd als niet meer toegewezen in de relevante PFS (Page Free Space)-toewijzingsbytemap.
Vanaf SQL Server 2000 SP3, wanneer u een DROP
. uitvoert of TRUNCATE
van een tabel ziet u slechts enkele logrecords die worden gegenereerd. Als u een minuut of zo wacht en vervolgens opnieuw in het transactielogboek kijkt, ziet u dat duizenden logboekrecords zijn gegenereerd door de uitgestelde-drop-bewerking, waarbij elk een pagina of een gebied ongedaan maakt. De operatie wordt volledig en efficiënt gelogd.
Hier is een voorbeeld, gebruikmakend van het scenario dat we hierboven hebben gemaakt:
CHECKPOINT; GO TRUNCATE TABLE [TestTable]; GO SELECT COUNT (*) AS N'LogRecCount' FROM fn_dblog (NULL, NULL); GO
Resultaten:
LogRecCount———–
25
Zoals u kunt zien, zijn er duidelijk geen logboekrecords die de toewijzing van de 10.000 pagina's plus 1250 extensies in de TestTable-tabel ongedaan maken.
Als ik een paar seconden wacht en dan de fn_dblog
. uitvoer code opnieuw, krijg ik:
———–
3811
Je vraagt je misschien af waarom er niet minstens 10.000 logrecords zijn - één voor elke pagina die wordt verwijderd. Dat komt omdat de paginadeallocaties zelfs efficiënt worden gelogd - met één logrecord dat de PFS-paginatoewijzingswijzigingen voor 8 opeenvolgende gegevensbestandspagina's weergeeft, in plaats van één logrecord voor elke gegevensbestandspagina die de toewijzingsstatus weergeeft die op de PFS-pagina verandert.
SQL Server probeert altijd zo min mogelijk transactielogboeken te produceren, maar houdt zich toch aan de regels over volledige of minimale logboekregistratie op basis van het huidige herstelmodel. Als u de daadwerkelijke logrecords wilt bekijken die zijn gegenereerd door de onthaak-en-overdracht en uitgestelde-drop-mechanismen, vervangt u gewoon * door COUNT (*) in de fn_dblog-code hierboven en zoekt u naar een transactie met de transactienaam ingesteld op DeferredAllocUnitDrop::Process
.
In toekomstige berichten zal ik de interne aspecten bespreken die ten grondslag liggen aan andere hardnekkige mythes rond prestatieaspecten van de SQL Server Storage Engine.