Database
 sql >> Base de Dados >  >> RDS >> Database

O mito de que DROP e TRUNCATE TABLE não são registrados


Existe um mito persistente no mundo do SQL Server de que tanto o DROP TABLE e TRUNCATE TABLE comandos não são registrados.

Eles não são. Ambos estão totalmente registrados, mas registrados com eficiência.

Você pode facilmente provar isso para si mesmo. Execute o seguinte código para configurar um banco de dados e uma tabela de teste e mostre que temos 10.000 linhas em nossa tabela:
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

Resultados:
Contagem de linhas
———–
10000
E então o código a seguir, que trunca a tabela em uma transação e verifica a contagem de linhas:
BEGIN TRAN;
GO
TRUNCATE TABLE [TestTable];
GO
 
SELECT
    COUNT (*) AS N'RowCount'
FROM
    [TestTable];
GO

Resultados:
Contagem de linhas
———–
0
Agora a mesa está vazia. Mas posso reverter a transação e colocar todos os dados novamente:
ROLLBACK TRAN;
GO
 
SELECT
    COUNT (*) AS N'RowCount'
FROM
    [TestTable];
GO

Resultados:
Contagem de linhas
———–
10000
Claramente o TRUNCATE operação deve ser registrada, caso contrário a operação de reversão não funcionaria.

Então, de onde vem o equívoco?

Ele vem do comportamento de DROP e TRUNCATE operações em grandes mesas. Eles serão concluídos quase instantaneamente, e se você olhar no log de transações usando fn_dblog logo em seguida, você verá apenas um pequeno número de registros de log gerados a partir da operação. Esse pequeno número não se correlaciona com o tamanho da tabela que está sendo truncada ou descartada, então parece que DROP e TRUNCATE operações não são registradas.

Mas eles estão totalmente registrados, como demonstrei acima. Então, onde estão os registros de log das operações?

A resposta é que os registros de log serão criados, mas não imediatamente, por um mecanismo chamado 'deferred drop', que foi adicionado no SQL Server 2000 SP3.

Quando uma tabela é eliminada ou truncada, todas as páginas do arquivo de dados alocadas para a tabela devem ser desalocadas. O mecanismo para isso antes do SQL Server 2000 SP3 era o seguinte:
Para cada extensão alocada à tabela

Iniciar

Adquira um bloqueio de alocação exclusivo na extensão



Teste o bloqueio de página para cada página na extensão (adquira o bloqueio no modo eXclusivo e solte-o imediatamente, certificando-se de que ninguém mais tenha a página bloqueada)



NÃO libere o bloqueio de extensão, garantindo que ninguém mais possa usar essa extensão



Mover para a próxima extensão

Fim
Como todos os bloqueios de extensão foram mantidos até o final da operação, e cada bloqueio ocupa uma pequena quantidade de memória, era possível que o gerenciador de bloqueio ficasse sem memória quando um DROP ou TRUNCATE de uma mesa muito grande ocorreu. Alguns clientes do SQL Server começaram a descobrir que estavam em condições de falta de memória no SQL Server 2000, pois as tabelas ficaram muito grandes e superaram amplamente o crescimento da memória do sistema.

O mecanismo de deferred-drop simula o DROP ou TRUNCATE operação concluída imediatamente, desvinculando as alocações para a tabela e colocando-as na 'fila de descarte diferido', para processamento posterior por uma tarefa em segundo plano. Essa operação de desconexão e transferência gera apenas alguns registros de log. Esta é a operação que está sendo feita e revertida no meu exemplo de código acima.
A 'tarefa em segundo plano de deferred-drop' gira a cada poucos segundos e desaloca todas as páginas e extensões na fila de deferred-drop em pequenas lotes, garantindo que a operação não ficará sem memória. Essas desalocações são todas totalmente registradas, mas lembre-se de que a desalocação de uma página cheia de dados ou registros de índice não registra exclusões individuais desses registros; em vez disso, a página inteira é apenas marcada como desalocada no mapa de bytes de alocação PFS (Page Free Space) relevante.

Do SQL Server 2000 SP3 em diante, quando você executa um DROP ou TRUNCATE de uma tabela, você verá apenas alguns registros de log sendo gerados. Se você esperar um minuto ou mais e, em seguida, olhar no log de transações novamente, verá que milhares de registros de log foram gerados pela operação de deferred-drop, cada um desalocando uma página ou extensão. A operação é completa e eficientemente registrada.

Aqui está um exemplo, usando o cenário que criamos acima:
CHECKPOINT;
GO
TRUNCATE TABLE [TestTable];
GO
SELECT
    COUNT (*) AS N'LogRecCount'
FROM
    fn_dblog (NULL, NULL);
GO

Resultados:
LogRecCount

———–

25
Como você pode ver, claramente não há registros de log desalocando as 10.000 páginas, mais 1.250 extensões na tabela TestTable.

Se eu esperar alguns segundos e executar o fn_dblog código novamente, recebo:
LogRecCount

———–

3811
Você pode se perguntar por que não há pelo menos 10.000 registros de log – um para cada página sendo desalocada. Isso ocorre porque as desalocações de página são registradas com eficiência – com um registro de log refletindo as alterações de alocação de página PFS para 8 páginas consecutivas de arquivo de dados, em vez de um registro de log para cada página de arquivo de dados refletindo seu status de alocação mudando na página PFS.

O SQL Server sempre tenta produzir o menor log de transações possível, ao mesmo tempo em que segue as regras sobre log completo ou mínimo com base no modelo de recuperação atual. Se você quiser ver os registros de log reais gerados pelos mecanismos unhook-and-transfer e deferred-drop, simplesmente substitua * por COUNT (*) no código fn_dblog acima e procure uma transação com o Nome da Transação definido como DeferredAllocUnitDrop::Process .

Em postagens futuras, discutirei os detalhes internos que sustentam outros mitos persistentes em torno dos aspectos de desempenho do SQL Server Storage Engine.