Tengo una tabla en una base de datos de producción que tiene un tamaño de 525 GB, de los cuales 383 GB no se utilizan:
Me gustaría recuperar parte de este espacio, pero, antes de jugar con el DB de producción, estoy probando algunas estrategias en una tabla idéntica en un DB de prueba con menos datos. Esta tabla tiene un problema similar:
Alguna información sobre la tabla:
- El factor de relleno se establece en 0
- Hay alrededor de 30 columnas
- Una de las columnas es una imagen de tipo LOB, y almacena archivos que varían en tamaño desde unos pocos KB hasta varios cientos de MB
- La tabla no tiene ningún índice hipotético asociado.
El servidor ejecuta SQL Server 2017 (RTM-GDR) (KB4505224) - 14.0.2027.2 (X64). La base de datos está utilizando el SIMPLE
modelo de recuperación.
Algunas cosas que he probado:
- La reconstrucción de los índices:
ALTER INDEX ALL ON dbo.MyTable REBUILD
. Esto tuvo un impacto insignificante. - La reorganización de los índices:
ALTER INDEX ALL ON dbo.MyTable REORGANIZE WITH(LOB_COMPACTION = ON)
. Esto tuvo un impacto insignificante. Copié la columna LOB en otra tabla, soltó la columna, volvió a crear la columna y volvió a copiar los datos (como se describe en esta publicación: Liberación de la tabla de SQL Server del espacio no utilizado ). Esto disminuyó el espacio no utilizado, pero parecía convertirlo en espacio usado:
Usó la utilidad bcp para exportar la tabla, truncarla y volver a cargarla (como se describe en esta publicación: Cómo liberar el espacio no utilizado para una tabla ). Esto también redujo el espacio no utilizado y aumentó el espacio utilizado en un grado similar al de la imagen de arriba.
- Aunque no se recomienda, probé los comandos DBCC SHRINKFILE y DBCC SHRINKDATABASE, pero no tuvieron ningún impacto en el espacio no utilizado.
- Correr
DBCC CLEANTABLE('myDB', 'dbo.myTable')
no hizo la diferencia - He intentado todo lo anterior mientras mantengo los tipos de datos de imagen y texto y después de cambiar los tipos de datos a varbinary (max) y varchar (max).
- Intenté importar los datos a una nueva tabla en una nueva base de datos, y esto también solo convirtió el espacio no utilizado en espacio usado. Describí los detalles de este intento en esta publicación .
No quiero hacer estos intentos en la base de datos de producción si estos son los resultados que puedo esperar, así que:
- ¿Por qué el espacio no utilizado se está convirtiendo en espacio usado después de algunos de estos intentos? Siento que no entiendo bien lo que sucede debajo del capó.
- ¿Hay algo más que pueda hacer para disminuir el espacio no utilizado sin aumentar el espacio utilizado?
EDITAR: Aquí está el informe de uso del disco y la secuencia de comandos para la tabla:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[MyTable](
[Column1] [int] NOT NULL,
[Column2] [int] NOT NULL,
[Column3] [int] NOT NULL,
[Column4] [bit] NOT NULL,
[Column5] [tinyint] NOT NULL,
[Column6] [datetime] NULL,
[Column7] [int] NOT NULL,
[Column8] [varchar](100) NULL,
[Column9] [varchar](256) NULL,
[Column10] [int] NULL,
[Column11] [image] NULL,
[Column12] [text] NULL,
[Column13] [varchar](100) NULL,
[Column14] [varchar](6) NULL,
[Column15] [int] NOT NULL,
[Column16] [bit] NOT NULL,
[Column17] [datetime] NULL,
[Column18] [varchar](50) NULL,
[Column19] [varchar](50) NULL,
[Column20] [varchar](60) NULL,
[Column21] [varchar](20) NULL,
[Column22] [varchar](120) NULL,
[Column23] [varchar](4) NULL,
[Column24] [varchar](75) NULL,
[Column25] [char](1) NULL,
[Column26] [varchar](50) NULL,
[Column27] [varchar](128) NULL,
[Column28] [varchar](50) NULL,
[Column29] [int] NULL,
[Column30] [text] NULL,
CONSTRAINT [PK] PRIMARY KEY CLUSTERED
(
[Column1] ASC,
[Column2] ASC,
[Column3] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[MyTable] ADD CONSTRAINT [DF_Column4] DEFAULT (0) FOR [Column4]
GO
ALTER TABLE [dbo].[MyTable] ADD CONSTRAINT [DF_Column5] DEFAULT (0) FOR [Column5]
GO
ALTER TABLE [dbo].[MyTable] ADD CONSTRAINT [DF_Column15] DEFAULT (0) FOR [Column15]
GO
ALTER TABLE [dbo].[MyTable] ADD CONSTRAINT [DF_Column16] DEFAULT (0) FOR [Column16]
GO
Estos son los resultados de ejecutar los comandos en la respuesta de Max Vernon:
╔════════════╦═══════════╦════════════╦═════════════════╦══════════════════════╦════════════════════╗
║ TotalBytes ║ FreeBytes ║ TotalPages ║ TotalEmptyPages ║ PageBytesFreePercent ║ UnusedPagesPercent ║
╠════════════╬═══════════╬════════════╬═════════════════╬══════════════════════╬════════════════════╣
║ 9014280192║ 8653594624║ 1100376║ 997178 ║ 95.998700 ║ 90.621500 ║
╚════════════╩═══════════╩════════════╩═════════════════╩══════════════════════╩════════════════════╝
╔═════════════╦═══════════════════╦════════════════════╗
║ ObjectName ║ ReservedPageCount ║ UsedPageCount ║
╠═════════════╬═══════════════════╬════════════════════╣
║ dbo.MyTable ║ 5109090 ║ 2850245 ║
╚═════════════╩═══════════════════╩════════════════════╝
ACTUALIZAR:
Ejecuté lo siguiente según lo sugerido por Max Vernon:
DBCC UPDATEUSAGE (N'<database_name>', N'<table_name>');
Y aquí estaba el resultado:
DBCC UPDATEUSAGE: Usage counts updated for table 'MyTable' (index 'PK_MyTable', partition 1):
USED pages (LOB Data): changed from (568025) to (1019641) pages.
RSVD pages (LOB Data): changed from (1019761) to (1019763) pages.
Esto actualizó el uso del disco para la tabla:
Y el uso general del disco:
Por lo tanto, parece que el problema fue que el uso del disco según lo rastreado por SQL Server se desincronizó con el uso real del disco. Consideraré este problema resuelto, ¡pero me interesaría saber por qué esto habría sucedido en primer lugar!
fuente
DBCC UPDATEUSAGE
actualizó el espacio no utilizado y el número de páginas no utilizadas. Parece que el uso del disco y la información de la página informada por SQL Server estaba extremadamente fuera de sincronización: actualicé mi publicación con los detalles. Tengo curiosidad por saber cómo habría sucedido esto en primer lugar, pero al menos se encontró el problema. Gracias por toda su ayuda, ¡realmente lo aprecio!Podría estar experimentando fragmentación interna.
¿Cuál es la fragmentación de la página para esta tabla?
¿Y es la fragmentación para la fila diferente de las páginas fuera de fila?
Dices que tienes archivos de unos pocos KB.
SQL Server almacena todo en páginas de 8060 bytes. Es decir, si tiene una fila (o datos fuera de fila) de 4040 bytes y la siguiente es similar, no puede caber en la misma página y desperdiciará la mitad de su espacio. Intente cambiar el tamaño de su fila almacenando columnas de longitud variable (comience con la imagen, por ejemplo) en una tabla diferente.
fuente
¿La base de datos está en modo de recuperación completa? Si es así, cuando realiza una reducción, está registrando todos los cambios y no lo reducirá de la manera esperada. Dependiendo de sus horas de operación, puede realizar una copia de seguridad, cambiar al modo de recuperación de envío masivo y luego ejecutar la reducción en el archivo de datos. Después de eso, querrás ejecutar scripts de índice para reparar / reconstruir y volver a la recuperación completa. Eso es lo que intentaría de todos modos, pero de nuevo, depende de sus horas de operación para todo esto.
fuente
La única vez que no he podido reducir una base de datos y reclamar espacio es porque no se puede reducir una base de datos más allá del tamaño inicial de la base de datos cuando se creó. Entonces, por ejemplo, si su base de datos es una copia de la base de datos de producción y creó la base de datos por primera vez a 525 GB, el servidor sql no le permitirá reducir el tamaño por debajo de 525 GB sin importar la cantidad de datos que elimine de la base de datos. Pero si el DB se creó por debajo de 383 GB y luego creció a 525 GB, no debería tener problemas para recuperar el espacio. Durante mucho tiempo pensé que esta es una restricción estúpida y arbitraria por parte de Microsoft.
Reduzca la base de datos solo hasta su tamaño inicial que se establece después de crear la base de datos
fuente
Me he encontrado con este problema antes en las cajas de producción, lo que debe hacer es reconstruir tablas e índices para cada tabla (en ese orden).
Aquí está la consulta que uso para mantener las tablas bajo control. Le ayudará a determinar qué tablas deben reconstruirse y a crear las consultas SQL que necesita ejecutar. Esta consulta se limita a aquellos con más de 1 MB de espacio sin usar y una proporción de 5% sin usar, por lo que solo reconstruye lo que realmente necesita para centrarse:
fuente