Existe un mito persistente en el mundo de SQL Server de que tanto la DROP TABLE
y TRUNCATE TABLE
los comandos no se registran.
Ellos no están. Ambos están completamente registrados, pero registrados de manera eficiente.
Puedes probar esto fácilmente por ti mismo. Ejecute el siguiente código para configurar una base de datos y una tabla de prueba, y demuestre que tenemos 10 000 filas en nuestra tabla:
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:
Número de filas———–
10000
Y luego el siguiente código, que trunca la tabla en una transacción y verifica el recuento de filas:
BEGIN TRAN; GO TRUNCATE TABLE [TestTable]; GO SELECT COUNT (*) AS N'RowCount' FROM [TestTable]; GO
Resultados:
Número de filas———–
0
Ahora la mesa está vacía. Pero puedo revertir la transacción y volver a colocar todos los datos:
ROLLBACK TRAN; GO SELECT COUNT (*) AS N'RowCount' FROM [TestTable]; GO
Resultados:
Número de filas———–
10000
Claramente el TRUNCATE
la operación debe registrarse; de lo contrario, la operación de reversión no funcionaría.
Entonces, ¿de dónde viene el concepto erróneo?
Viene del comportamiento de DROP
y TRUNCATE
operaciones en tablas grandes. Se completarán casi instantáneamente, y si miras en el registro de transacciones usando fn_dblog
inmediatamente después, solo verá una pequeña cantidad de registros generados a partir de la operación. Ese pequeño número no se correlaciona con el tamaño de la tabla que se trunca o elimina, por lo que parece que DROP
y TRUNCATE
las operaciones no se registran.
Pero están completamente registrados, como demostré anteriormente. Entonces, ¿dónde están los registros de las operaciones?
La respuesta es que los registros se crearán, pero no de inmediato, mediante un mecanismo llamado "caída diferida", que se agregó en SQL Server 2000 SP3.
Cuando una tabla se descarta o se trunca, todas las páginas del archivo de datos asignadas a la tabla deben desasignarse. El mecanismo para esto antes de SQL Server 2000 SP3 era el siguiente:
Para cada extensión asignada a la tablaComenzar
Adquirir un bloqueo de asignación exclusivo en la extensión
Sondear la bloqueo de página para cada página en la medida (adquirir el bloqueo en modo eXclusivo e inmediatamente soltarlo, asegurándose de que nadie más tenga la página bloqueada)
NO libere el bloqueo de extensión, garantizando que nadie más pueda usar esa extensión
Mover a la siguiente extensión
Fin
Como todos los bloqueos de extensión se mantuvieron hasta el final de la operación, y cada bloqueo ocupa una pequeña cantidad de memoria, era posible que el administrador de bloqueos se quedara sin memoria cuando DROP
o TRUNCATE
de una mesa muy grande ocurrió. Algunos clientes de SQL Server empezaron a darse cuenta de que se encontraban con situaciones de falta de memoria en SQL Server 2000, ya que las tablas crecían mucho y superaban con creces el crecimiento de la memoria del sistema.
El mecanismo de caída diferida simula el DROP
o TRUNCATE
la operación se completa de inmediato, desenganchando las asignaciones para la tabla y colocándolas en la "cola de entrega diferida", para su posterior procesamiento por una tarea en segundo plano. Esta operación de desenganche y transferencia solo genera un puñado de registros. Esta es la operación que se está realizando y revirtiendo en mi ejemplo de código anterior.
La 'tarea en segundo plano de colocación diferida' gira cada pocos segundos y desasigna todas las páginas y extensiones en la cola de colocación diferida en pequeños lotes, garantizando que la operación no se quedará sin memoria. Estas desasignaciones están completamente registradas, pero recuerde que desasignar una página llena de datos o registros de índice no registra eliminaciones individuales de esos registros; en su lugar, toda la página se marca como desasignada en el mapa de bytes de asignación de PFS (espacio libre de página) correspondiente.
Desde SQL Server 2000 SP3 en adelante, cuando realiza un DROP
o TRUNCATE
de una tabla, solo verá que se generan algunos registros. Si espera un minuto más o menos y luego vuelve a mirar el registro de transacciones, verá que la operación de colocación diferida ha generado miles de entradas de registro, cada una de las cuales desasigna una página o extensión. La operación se registra completa y eficientemente.
Aquí hay un ejemplo, usando el escenario que creamos arriba:
CHECKPOINT; GO TRUNCATE TABLE [TestTable]; GO SELECT COUNT (*) AS N'LogRecCount' FROM fn_dblog (NULL, NULL); GO
Resultados:
LogRecCount———–
25
Como puede ver, claramente no hay registros que desasignen las 10 000 páginas, más 1250 extensiones en la tabla TestTable.
Si espero unos segundos y luego ejecuto fn_dblog
código de nuevo, obtengo:
———–
3811
Quizás se pregunte por qué no hay al menos 10 000 registros, uno para cada página que se desasigna. Esto se debe a que las desasignaciones de páginas incluso se registran de manera eficiente, con un registro de registro que refleja los cambios de asignación de páginas de PFS para 8 páginas de archivo de datos consecutivas, en lugar de un registro de registro para cada página de archivo de datos que refleja el cambio de su estado de asignación en la página de PFS.
SQL Server siempre trata de generar el menor registro de transacciones posible, al mismo tiempo que se adhiere a las reglas sobre el registro mínimo o completo según el modelo de recuperación actual. Si desea ver los registros reales generados por los mecanismos de desenganche y transferencia y caída diferida, simplemente sustituya * por COUNT (*) en el código fn_dblog anterior y busque una transacción con el Nombre de transacción establecido en DeferredAllocUnitDrop::Process
.
En publicaciones futuras, analizaré los aspectos internos que sustentan otros mitos persistentes sobre los aspectos de rendimiento del motor de almacenamiento de SQL Server.