Nous avons plusieurs bases de données dans lesquelles un grand nombre de tables sont créées et supprimées. D'après ce que nous pouvons en dire, SQL Server n'effectue aucune maintenance interne sur les tables de base du système , ce qui signifie qu'elles peuvent devenir très fragmentées au fil du temps et gonflées en taille. Cela exerce une pression inutile sur le pool de mémoire tampon et a également un impact négatif sur les performances des opérations telles que le calcul de la taille de toutes les tables d'une base de données.
Quelqu'un a-t-il des suggestions pour minimiser la fragmentation sur ces tables internes principales? Une solution évidente pourrait éviter de créer autant de tables (ou de créer toutes les tables transitoires dans tempdb), mais pour les besoins de cette question, disons que l'application n'a pas cette flexibilité.
Edit: Des recherches supplémentaires montrent cette question sans réponse , qui semble étroitement liée et indique qu'une certaine forme de maintenance manuelle via ALTER INDEX...REORGANIZE
peut être une option.
Recherches initiales
Les métadonnées sur ces tables peuvent être consultées dans 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
Cependant, sys.dm_db_index_physical_stats
ne semble pas prendre en charge l'affichage de la fragmentation de ces tables:
-- 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'
)
Les scripts d'Ola Hallengren contiennent également un paramètre pour prendre en compte la défragmentation des is_ms_shipped = 1
objets, mais la procédure ignore silencieusement les tables de base du système même si ce paramètre est activé. Ola a précisé qu'il s'agit du comportement attendu; seules les tables utilisateur (pas les tables système) qui sont ms_shipped (par exemple msdb.dbo.backupset
) sont prises en compte.
-- 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);
Informations supplémentaires demandées
J'ai utilisé une adaptation de la requête d'Aaron sous l'inspection de l'utilisation du pool de mémoire tampon de table système, et cela a révélé qu'il y a des dizaines de Go de tables système dans le pool de mémoire tampon pour une seule base de données, avec environ 80% de cet espace étant de l'espace libre dans certains cas .
-- 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