Database
 sql >> Database >  >> RDS >> Database

Il mito che DROP e TRUNCATE TABLE non sono registrati

C'è un mito persistente nel mondo di SQL Server che sia il DROP TABLE e TRUNCATE TABLE i comandi non sono registrati.

Loro non sono. Sono entrambi completamente registrati, ma registrati in modo efficiente.

Puoi facilmente dimostrarlo a te stesso. Esegui il codice seguente per configurare un database e una tabella di test e mostra che abbiamo 10.000 righe nella nostra tabella:

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

Risultati:

Conteggio righe
———–
10000

E poi il codice seguente, che tronca la tabella in una transazione e controlla il conteggio delle righe:

BEGIN TRAN;
GO
TRUNCATE TABLE [TestTable];
GO
 
SELECT
    COUNT (*) AS N'RowCount'
FROM
    [TestTable];
GO

Risultati:

Conteggio righe
———–
0

Ora il tavolo è vuoto. Ma posso annullare la transazione e reinserire tutti i dati:

ROLLBACK TRAN;
GO
 
SELECT
    COUNT (*) AS N'RowCount'
FROM
    [TestTable];
GO

Risultati:

Conteggio righe
———–
10000

Chiaramente il TRUNCATE l'operazione deve essere registrata altrimenti l'operazione di rollback non funzionerebbe.

Allora da dove viene l'idea sbagliata?

Deriva dal comportamento di DROP e TRUNCATE operazioni su grandi tavoli. Verranno completati quasi istantaneamente e se guardi nel registro delle transazioni utilizzando fn_dblog subito dopo, vedrai solo un piccolo numero di record di registro generati dall'operazione. Quel piccolo numero non è correlato alla dimensione della tabella che viene troncata o eliminata, quindi sembra che DROP e TRUNCATE le operazioni non sono registrate.

Ma sono completamente registrati, come ho dimostrato sopra. Allora, dove sono i record di registro per le operazioni?

La risposta è che i record di registro verranno creati, ma non immediatamente, da un meccanismo chiamato "rilascio differito", che è stato aggiunto in SQL Server 2000 SP3.

Quando una tabella viene eliminata o troncata, tutte le pagine del file di dati allocate per la tabella devono essere deallocate. Il meccanismo per questo prima di SQL Server 2000 SP3 era il seguente:

Per ogni extent allocato alla tabella

Inizia

Acquisire un blocco di allocazione esclusivo sull'extent



Sondare il blocco della pagina per ogni pagina nell'estensione (acquisisci il blocco in modalità esclusiva e rilascialo immediatamente, assicurandoti che nessun altro abbia la pagina bloccata)



NON rilasciare il blocco dell'estensione, garantendo che nessun altro possa utilizzare tale estensione



Passa all'estensione successiva

Fine

Poiché tutti i blocchi di estensione sono stati mantenuti fino alla fine dell'operazione e ogni blocco richiede una piccola quantità di memoria, era possibile che il gestore dei blocchi esaurisse la memoria quando un DROP o TRUNCATE si è verificato un tavolo molto grande. Alcuni clienti di SQL Server hanno iniziato a scoprire di aver riscontrato condizioni di memoria insufficiente su SQL Server 2000, poiché le tabelle sono diventate molto grandi e hanno superato di gran lunga la crescita della memoria di sistema.

Il meccanismo di rilascio differito simula il DROP o TRUNCATE operazione completata immediatamente, sganciando le allocazioni per la tabella e mettendole nella "coda di rilascio differito", per una successiva elaborazione da parte di un'attività in background. Questa operazione di sgancio e trasferimento genera solo una manciata di record di registro. Questa è l'operazione che viene eseguita e ripristinata nel mio esempio di codice sopra.
L'"attività in background di rilascio posticipato" si avvia ogni pochi secondi e dealloca tutte le pagine e gli extent sulla coda di rilascio posticipato in piccolo batch, garantendo che l'operazione non esaurisca la memoria. Queste deallocazioni sono tutte completamente registrate, ma ricorda che la deallocazione di una pagina piena di dati o record di indicizzazione non registra le singole eliminazioni di tali record; invece l'intera pagina viene semplicemente contrassegnata come deallocata nella relativa mappa dei byte di allocazione PFS (Page Free Space).

Da SQL Server 2000 SP3 in poi, quando si esegue un DROP o TRUNCATE di una tabella, vedrai solo alcuni record di registro generati. Se aspetti circa un minuto e poi guardi di nuovo nel registro delle transazioni, vedrai che migliaia di record di registro sono stati generati dall'operazione di rilascio differito, ciascuno dei quali dealloca una pagina o un'estensione. L'operazione viene registrata in modo completo ed efficiente.

Ecco un esempio, utilizzando lo scenario che abbiamo creato sopra:

CHECKPOINT;
GO
TRUNCATE TABLE [TestTable];
GO
SELECT
    COUNT (*) AS N'LogRecCount'
FROM
    fn_dblog (NULL, NULL);
GO

Risultati:

LogRecCount

———–

25

Come puoi vedere, chiaramente non ci sono record di registro che deallocano le 10.000 pagine, più 1.250 estensioni nella tabella TestTable.

Se aspetto qualche secondo, quindi eseguo il fn_dblog codice di nuovo, ottengo:

LogRecCount

———–

3811

Potresti chiederti perché non ci sono almeno 10.000 record di registro, uno per ogni pagina che viene deallocata. Questo perché le deallocazioni delle pagine vengono persino registrate in modo efficiente, con un record di registro che riflette le modifiche all'allocazione della pagina PFS per 8 pagine di file di dati consecutive, invece di un record di registro per ciascuna pagina di file di dati che riflette il cambiamento dello stato di allocazione nella pagina PFS.

SQL Server tenta sempre di produrre il minor registro delle transazioni possibile, pur continuando a rispettare le regole sulla registrazione completa o minima in base al modello di ripristino corrente. Se vuoi guardare i record di log effettivi generati dai meccanismi di sgancio e trasferimento e rilascio differito, sostituisci semplicemente * con COUNT (*) nel codice fn_dblog sopra e cerca una transazione con il nome transazione impostato su DeferredAllocUnitDrop::Process .

Nei post futuri parlerò degli elementi interni che sono alla base di altri miti persistenti sugli aspetti delle prestazioni di SQL Server Storage Engine.