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
la source
Sur la base des conseils de la réponse d'Aaron ainsi que de recherches supplémentaires, voici un bref résumé de l'approche que j'ai adoptée.
D'après ce que je peux dire, les options d'inspection de la fragmentation des tables de base du système sont limitées. Je suis allé de l'avant et j'ai déposé un problème de connexion pour offrir une meilleure visibilité, mais en attendant, il semble que les options incluent des choses comme l'examen du pool de mémoire tampon ou la vérification du nombre moyen d'octets par ligne.
J'ai ensuite créé une procédure pour effectuer `ALTER INDEX ... REORGANIZE sur toutes les tables de base du système . L'exécution de cette procédure sur quelques-uns de nos serveurs de développement les plus (ab) utilisés a montré que la taille cumulée des tables de base du système était réduite jusqu'à 50 Go (avec ~ 5 MM de tables d'utilisateurs sur le système, donc clairement un cas extrême).
L'une de nos tâches de maintenance nocturne, qui aide à nettoyer de nombreuses tables d'utilisateurs créées par divers tests unitaires et développement, prenait auparavant environ 50 minutes. Une combinaison de
sp_whoisactive
,sys.dm_os_waiting_tasks
et aDBCC PAGE
montré que les attentes étaient dominées par les E / S sur les tables de base du système.Après la réorganisation de toutes les tables de base du système, la tâche de maintenance est tombée à environ 15 minutes. Il y avait encore quelques attentes d'E / S, mais elles ont été considérablement réduites, peut-être en raison d'une plus grande quantité de données restant dans le cache et / ou de plus de lectures en raison d'une fragmentation plus faible.
Par conséquent, ma conclusion est que l'ajout
ALTER INDEX...REORGANIZE
de tables de base système dans un plan de maintenance peut être une chose utile à considérer, mais probablement uniquement si vous avez un scénario dans lequel un nombre inhabituel d'objets est créé sur une base de données.la source