Tenemos varias bases de datos en las que se crean y descartan una gran cantidad de tablas. Por lo que podemos decir, SQL Server no realiza ningún mantenimiento interno en las tablas base del sistema , lo que significa que pueden fragmentarse con el tiempo y aumentar de tamaño. Esto ejerce una presión innecesaria sobre el grupo de búferes y también afecta negativamente el rendimiento de las operaciones, como calcular el tamaño de todas las tablas de una base de datos.
¿Alguien tiene sugerencias para minimizar la fragmentación en estas tablas internas básicas? Una solución obvia podría ser evitar crear tantas tablas (o crear todas las tablas transitorias en tempdb), pero para el propósito de esta pregunta, digamos que la aplicación no tiene esa flexibilidad.
Editar: La investigación adicional muestra esta pregunta sin respuesta , que parece estar estrechamente relacionada e indica que alguna forma de mantenimiento manual ALTER INDEX...REORGANIZE
puede ser una opción.
Investigación inicial
Los metadatos sobre estas tablas se pueden ver en sys.dm_db_partition_stats
:
-- The system base table that contains one row for every column in the system
SELECT row_count,
(reserved_page_count * 8 * 1024.0) / row_count AS bytes_per_row,
reserved_page_count/128. AS space_mb
FROM sys.dm_db_partition_stats
WHERE object_id = OBJECT_ID('sys.syscolpars')
AND index_id = 1
-- row_count: 15,600,859
-- bytes_per_row: 278.08
-- space_mb: 4,136
Sin embargo, sys.dm_db_index_physical_stats
no parece admitir la visualización de la fragmentación de estas tablas:
-- No fragmentation data is returned by sys.dm_db_index_physical_stats
SELECT *
FROM sys.dm_db_index_physical_stats(
DB_ID(),
OBJECT_ID('sys.syscolpars'),
NULL,
NULL,
'DETAILED'
)
Los scripts de Ola Hallengren también contienen un parámetro para considerar la desfragmentación de los is_ms_shipped = 1
objetos, pero el procedimiento ignora silenciosamente las tablas base del sistema incluso con este parámetro habilitado. Ola aclaró que este es el comportamiento esperado; solo se consideran las tablas de usuario (no las tablas del sistema) que se envían ms (por ejemplo msdb.dbo.backupset
).
-- Returns code 0 (successful), but does not do any work for system base tables.
-- Instead of the expected commands to update statistics and reorganize indexes,
-- no commands are generated. The script seems to assume the target tables will
-- appear in sys.tables, but this does not appear to be a valid assumption for
-- system tables like sys.sysrowsets or sys.syscolpars.
DECLARE @result int;
EXEC @result = IndexOptimize @Databases = 'Test',
@FragmentationLow = 'INDEX_REORGANIZE',
@FragmentationMedium = 'INDEX_REORGANIZE',
@FragmentationHigh = 'INDEX_REORGANIZE',
@PageCountLevel = 0,
@UpdateStatistics = 'ALL',
@Indexes = '%Test.sys.sysrowsets.%',
-- Proc works properly if targeting a non-system table instead
--@Indexes = '%Test.dbo.Numbers.%',
@MSShippedObjects = 'Y',
@Execute = 'N';
PRINT(@result);
Información adicional solicitada
Utilicé una adaptación de la consulta de Aaron debajo del uso del grupo de búfer de inspección de la tabla del sistema, y descubrí que hay decenas de GB de tablas del sistema en el grupo de búfer para solo una base de datos, con ~ 80% de ese espacio siendo espacio libre en algunos casos .
-- Compute buffer pool usage by system table
SELECT OBJECT_NAME(p.object_id),
COUNT(b.page_id) pages,
SUM(b.free_space_in_bytes/8192.0) free_pages
FROM sys.dm_os_buffer_descriptors b
JOIN sys.allocation_units a
ON a.allocation_unit_id = b.allocation_unit_id
JOIN sys.partitions p
ON p.partition_id = a.container_id
AND p.object_id < 1000 -- A loose proxy for system tables
WHERE b.database_id = DB_ID()
GROUP BY p.object_id
ORDER BY pages DESC
fuente
Basado en la guía de la respuesta de Aaron, así como en investigaciones adicionales, aquí hay una descripción rápida del enfoque que tomé.
Por lo que puedo decir, las opciones para inspeccionar la fragmentación de las tablas base del sistema son limitadas. Seguí adelante y presenté un problema de Connect para proporcionar una mejor visibilidad, pero mientras tanto parece que las opciones incluyen cosas como examinar el grupo de búferes o verificar el número promedio de bytes por fila.
Luego creé un procedimiento para realizar `ALTER INDEX ... REORGANIZE en todas las tablas base del sistema . La ejecución de este procedimiento en algunos de nuestros servidores de desarrollo más (ab) utilizados mostró que el tamaño acumulativo de las tablas base del sistema se redujo hasta en 50 GB (con ~ 5MM de tablas de usuario en el sistema, por lo que claramente es un caso extremo).
Una de nuestras tareas de mantenimiento nocturno, que ayuda a limpiar muchas de las tablas de usuarios creadas por varias pruebas unitarias y desarrollo, antes tardaba unos 50 minutos en completarse. Una combinación de
sp_whoisactive
,sys.dm_os_waiting_tasks
yDBCC PAGE
mostró que las esperas estaban dominadas por E / S en las tablas base del sistema.Después de la reorganización de todas las tablas base del sistema, la tarea de mantenimiento se redujo a ~ 15 minutos. Todavía había algunas esperas de E / S, pero se redujeron significativamente, tal vez debido a una mayor cantidad de datos que permanecen en la memoria caché y / o más leídos debido a una menor fragmentación.
Por lo tanto, mi conclusión es que agregar
ALTER INDEX...REORGANIZE
tablas base del sistema en un plan de mantenimiento puede ser algo útil a considerar, pero probablemente solo si tiene un escenario en el que se está creando una cantidad inusual de objetos en una base de datos.fuente