Tengo una tabla con 64 millones de filas que toman 4,3 GB en disco para sus datos.
Cada fila tiene aproximadamente 30 bytes de columnas enteras, más una NVARCHAR(255)
columna variable para texto.
Agregué una columna NULLABLE con tipo de datos Datetimeoffset(0)
.
Luego ACTUALIZÉ esta columna para cada fila y me aseguré de que todas las nuevas inserciones coloquen un valor en esta columna.
Una vez que no hubo entradas NULL, ejecuté este comando para hacer que mi nuevo campo sea obligatorio:
ALTER TABLE tblCheckResult
ALTER COLUMN [dtoDateTime] [datetimeoffset](0) NOT NULL
El resultado fue un enorme crecimiento en el tamaño del registro de transacciones, de 6GB a más de 36GB hasta que se quedó sin espacio.
¿Alguien tiene alguna idea de lo que está haciendo SQL Server 2008 R2 para que este simple comando resulte en un crecimiento tan grande?
fuente
NOT NULL
columna con una operación predeterminada como metadatos. Consulte también "Agregar columnas NO NULL como una operación en línea" en la documentación .Respuestas:
Cuando cambia una columna a NOT NULL, SQL Server tiene que tocar cada página, incluso si no hay valores NULL. Dependiendo de su factor de relleno, esto podría conducir a muchas divisiones de página. Cada página que se toca, por supuesto, debe registrarse, y sospecho que debido a las divisiones, es posible que se deban registrar dos cambios para muchas páginas. Sin embargo, dado que todo se hace en una sola pasada, el registro debe tener en cuenta todos los cambios para que, si presiona cancelar, sepa exactamente qué deshacer.
Un ejemplo. Tabla simple:
Ahora, veamos los detalles de la página. Primero tenemos que averiguar qué página y DB_ID estamos tratando. En mi caso, creé una base de datos llamada
foo
y el DB_ID resultó ser 5.La salida indicaba que estaba interesado en la página 159 (la única fila en la
DBCC IND
salida conPageType = 1
).Ahora, veamos algunos detalles de la página seleccionada a medida que avanzamos por el escenario del OP.
Ahora, no tengo todas las respuestas a esto, ya que no soy un tipo interno profundo. Pero está claro que, si bien tanto la operación de actualización como la adición de la restricción NOT NULL, sin lugar a dudas, escriben en la página, este último lo hace de una manera completamente diferente. Parece que en realidad cambia la estructura del registro, en lugar de solo jugar con bits, cambiando la columna anulable por una columna no anulable. Por qué tiene que hacer eso, no estoy muy seguro: una buena pregunta para el equipo del motor de almacenamiento , supongo. Creo que SQL Server 2012 maneja algunos de estos escenarios mucho mejor, FWIW, pero todavía tengo que hacer pruebas exhaustivas.
fuente
Al ejecutar el comando
Esto parece implementarse como una operación Agregar columna, Actualizar, Eliminar columna.
sys.sysrscols
para representar una nueva columna. Elstatus
bit para128
se establece indicando que la columna no permiteNULL
ssys.sysrscols
.rscolid
Actualizado a un entero grande y elstatus
bit 2 configurado como indicado)sys.sysrscols
para la nueva columna se modifica para darle elrscolid
de la columna anterior.La operación que tiene el potencial de causar muchos registros es la
UPDATE
de todas las filas de la tabla, sin embargo, eso no significa que esto siempre ocurra. Si las imágenes "antes" y "después" de la fila son idénticas, esto se tratará como una actualización que no se actualiza y no se registrará desde mi prueba hasta el momento.Por lo tanto, la explicación de por qué está obteniendo muchos registros dependerá de por qué exactamente las versiones "antes" y "después" de la fila no son las mismas.
Para las columnas de longitud variable almacenadas en el
FixedVar
formato, encontré que la configuraciónNOT NULL
siempre provoca un cambio en la fila que debe registrarse. El recuento de columnas y el recuento de columnas de longitud variable se incrementan y la nueva columna se agrega al final de la sección de longitud variable duplicando los datos.datetimeoffset(0)
es, sin embargo, de longitud fija y para las columnas de longitud fija almacenadas en elFixedVar
formato, las columnas antiguas y nuevas parecen tener el mismo espacio en la porción de datos de longitud fija de la fila y, como ambas tienen la misma longitud y valor, el "antes" y Las versiones "después" de la fila son las mismas . Esto se puede ver en la respuesta de @ Aaron. Ambas versiones de la fila antes y después delALTER TABLE dbo.floob ALTER COLUMN bar INT NOT NULL;
sonEsto no está registrado.
Lógicamente, a partir de mi descripción de los eventos, la fila debería ser diferente aquí, ya que el recuento de columnas
02
debería aumentarse,03
pero en la práctica no ocurre tal cambio.Algunas razones posibles de por qué esto puede ocurrir en una columna de longitud fija son
SPARSE
entonces, la nueva columna se almacenaría en una parte diferente de la fila del original, lo que provocaría que las imágenes de la fila anterior y posterior fueran diferentes.ALTER TABLE
operación previa que se implementó como un cambio de metadatos solamente y que aún no se ha aplicado a la fila. Por ejemplo, si se agregó una nueva columna de longitud variable anulable, esto se aplica originalmente como un cambio de metadatos y solo se escribe en las filas la próxima vez que se actualizan (la escritura que realmente ocurre en esta última instancia es solo actualizaciones para la sección de recuento de columnas yNULL_BITMAP
comoNULL
varchar
columna al final de la fila no ocupan espacio)fuente
Me enfrenté al mismo problema con respecto a una tabla que tenía 200,000,000 de filas. Inicialmente agregué la columna anulable, luego actualicé todas las filas y finalmente modifiqué la columna a
NOT NULL
través de unaALTER TABLE ALTER COLUMN
declaración. Esto resultó en dos grandes transacciones que explotaron el archivo de registro increíblemente (crecimiento de 170 GB).La forma más rápida que encontré fue la siguiente:
Agregue la columna usando un valor predeterminado
Descarte la restricción predeterminada utilizando SQL dinámico ya que la restricción no se ha nombrado antes:
El tiempo de ejecución bajó de> 30 minutos a 10 minutos, incluida la replicación de los cambios a través de la replicación transaccional. Estoy ejecutando una instalación de SQL Server 2008 (SP2).
fuente
Ejecuté la siguiente prueba:
Creo que esto tiene que ver con el espacio reservado que contiene el registro en caso de que revierta la transacción. Mire en la función fn_dblog en la columna 'Log Reserve' para la fila LOP_BEGIN_XACT y vea cuánto espacio está tratando de reservar.
fuente
select * FROM fn_dblog(null, null) where AllocUnitName='dbo.tblCheckResult' AND Operation = 'LOP_MODIFY_ROW'
, puedes ver las actualizaciones de 10000 filas.El comportamiento para esto es diferente en SQL Server 2012. Ver http://rusanu.com/2011/07/13/online-non-null-with-values-column-add-in-sql-server-11/
El número de registros generados para SQL Server 2008 R2 y versiones posteriores será significativamente mayor que el número de registros para SQL Server 2012.
fuente
NOT NULL
provocar el registro. El cambio en 2012 se trata de agregar una nuevaNOT NULL
columna con un valor predeterminado.