Det finns en ihållande myt i SQL Server-världen att både DROP TABLE
och TRUNCATE TABLE
kommandon loggas inte.
Dom är inte. De är båda helt loggade, men effektivt loggade.
Du kan enkelt bevisa detta för dig själv. Kör följande kod för att skapa en testdatabas och tabell och visa att vi har 10 000 rader i vår tabell:
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
Resultat:
RowCount———–
10 000
Och sedan följande kod, som trunkerar tabellen i en transaktion och kontrollerar radantalet:
BEGIN TRAN; GO TRUNCATE TABLE [TestTable]; GO SELECT COUNT (*) AS N'RowCount' FROM [TestTable]; GO
Resultat:
RowCount———–
0
Nu är bordet tomt. Men jag kan återställa transaktionen och lägga tillbaka all data igen:
ROLLBACK TRAN; GO SELECT COUNT (*) AS N'RowCount' FROM [TestTable]; GO
Resultat:
RowCount———–
10 000
Tydligt TRUNCATE
operationen måste loggas annars skulle återställningsoperationen inte fungera.
Så var kommer missuppfattningen ifrån?
Det kommer från beteendet hos DROP
och TRUNCATE
operationer på stora bord. De kommer att slutföras nästan omedelbart, och om du tittar i transaktionsloggen med fn_dblog
direkt efteråt ser du bara ett litet antal loggposter som genererats från operationen. Det lilla antalet korrelerar inte med storleken på tabellen som trunkeras eller tas bort, så det verkar som om DROP
och TRUNCATE
operationer loggas inte.
Men de är helt loggade, som jag visade ovan. Så var är loggposterna för operationerna?
Svaret är att loggposterna kommer att skapas, bara inte omedelbart, av en mekanism som kallas "deferred drop", som lades till i SQL Server 2000 SP3.
När en tabell släpps eller trunkeras måste alla datafilsidor som allokerats för tabellen avallokeras. Mekanismen för detta innan SQL Server 2000 SP3 var följande:
För varje omfattning som allokeras till tabellenBörja
Skaffa ett exklusivt tilldelningslås på omfattningen
Undersök sidlås för varje sida i omfattningen (skaffa låset i exklusivt läge och släpp det omedelbart, se till att ingen annan har sidan låst)
Gör INTE släpp omfattningslåset och garanterar att ingen annan kan använda den omfattningen
Flytta till nästa utsträckning
Sluta
Eftersom alla omfattningslås hölls kvar till slutet av operationen, och varje lås tar en liten mängd minne, var det möjligt för låshanteraren att få slut på minne när en DROP
eller TRUNCATE
av ett mycket stort bord inträffade. Vissa SQL Server-kunder började upptäcka att de hade slut på minnet på SQL Server 2000, eftersom tabellerna blev mycket stora och kraftigt överträffade tillväxten i systemminnet.
Mekanismen för uppskjuten släpp simulerar DROP
eller TRUNCATE
operationen slutförs omedelbart, genom att koppla loss allokeringarna för tabellen och placera dem i "uppskjuten släpp-kö", för senare bearbetning av en bakgrundsuppgift. Denna avkrok-och-överföringsoperation genererar bara en handfull loggposter. Det här är operationen som görs och rullas tillbaka i mitt kodexempel ovan.
Bakgrundsuppgiften "uppskjuten släpp" snurrar upp med några sekunders mellanrum och avallokerar alla sidor och omfattningar i kön för uppskjuten släpp i små batcher, vilket garanterar att operationen inte tar slut på minnet. Dessa avallokeringar är alla fullständigt loggade, men kom ihåg att om tilldelning av en sida full av data eller indexposter inte loggas individuella borttagningar av dessa poster; istället är hela sidan bara markerad som avallokerad i relevant PFS (Page Free Space) allokeringsbyte-karta.
Från SQL Server 2000 SP3 och framåt, när du utför en DROP
eller TRUNCATE
i en tabell ser du bara ett fåtal loggposter som genereras. Om du väntar en minut eller så och sedan tittar i transaktionsloggen igen, kommer du att se tusentals loggposter som har genererats av den uppskjutna släpp-operationen, var och en avfördelar en sida eller omfattning. Operationen loggas fullständigt och effektivt.
Här är ett exempel med scenariot vi skapade ovan:
CHECKPOINT; GO TRUNCATE TABLE [TestTable]; GO SELECT COUNT (*) AS N'LogRecCount' FROM fn_dblog (NULL, NULL); GO
Resultat:
LogRecCount———–
25
Som du kan se finns det uppenbarligen inga loggposter som deallokerar de 10 000 sidorna plus 1 250 omfattningar i testtabellen.
Om jag väntar några sekunder och sedan kör fn_dblog
kod igen, jag får:
———–
3811
Du kanske undrar varför det inte finns minst 10 000 loggposter – en för varje sida som deallokeras. Det beror på att sidtilldelningarna till och med loggas effektivt – med en loggpost som återspeglar PFS-sidallokeringsändringar för 8 på varandra följande datafilsidor, istället för en loggpost för varje datafilsida som återspeglar dess tilldelningsstatus som ändras på PFS-sidan.
SQL Server försöker alltid producera så lite transaktionslogg som möjligt, samtidigt som man följer reglerna om full eller minimal loggning baserat på den aktuella återställningsmodellen. Om du vill titta på de faktiska loggposterna som genereras av unhook-and-transfer och deferred-drop-mekanismerna, ersätter du helt enkelt * med COUNT (*) i fn_dblog-koden ovan och letar efter en transaktion med Transaktionsnamnet inställt på DeferredAllocUnitDrop::Process
.
I framtida inlägg kommer jag att diskutera de interna funktionerna som underbygger andra ihärdiga myter kring prestandaaspekter av SQL Server Storage Engine.