Un élément commun utilisé dans la conception de bases de données est la contrainte. Les contraintes se présentent sous différentes formes (par exemple, par défaut, unique) et renforcent l'intégrité de la ou des colonnes sur lesquelles elles existent. Lorsqu'elles sont bien implémentées, les contraintes sont un composant puissant dans la conception d'une base de données car elles empêchent les données qui ne répondent pas aux critères définis d'entrer dans une base de données. Cependant, les contraintes peuvent être violées à l'aide de commandes telles que WITH NOCHECK
et IGNORE_CONSTRAINTS
. De plus, lors de l'utilisation du REPAIR_ALLOW_DATA_LOSS
option avec n'importe quel DBCC CHECK
commande pour réparer la corruption de la base de données, les contraintes ne sont pas prises en compte.
Par conséquent, il est possible d'avoir des données invalides dans la base de données - soit des données qui ne respectent pas une contrainte, soit des données qui ne maintiennent plus la relation attendue entre la clé primaire et la clé étrangère. SQL Server inclut les DBCC CHECKCONSTRAINTS
déclaration pour trouver des données qui violent les contraintes. Après l'exécution de toute option de réparation, exécutez DBCC CHECKCONSTRAINTS
pour l'ensemble de la base de données afin de s'assurer qu'il n'y a pas de problèmes, et il peut y avoir des moments où il est approprié d'exécuter CHECKCONSTRAINTS
pour une contrainte de sélection ou une table. Le maintien de l'intégrité des données est essentiel, et même s'il n'est pas courant d'exécuter DBCC CHECKCONSTRAINTS
sur une base planifiée pour trouver des données non valides, lorsque vous avez besoin de l'exécuter, c'est une bonne idée de comprendre l'impact sur les performances qu'il peut créer.
DBCC CHECKCONSTRAINTS
peut s'exécuter pour une seule contrainte, une table ou la base de données entière. Comme d'autres commandes de vérification, son exécution peut prendre beaucoup de temps et consommer des ressources système, en particulier pour les bases de données plus volumineuses. Contrairement aux autres commandes de vérification, CHECKCONSTRAINTS
n'utilise pas d'instantané de base de données.
Avec les événements étendus, nous pouvons examiner l'utilisation des ressources lorsque nous exécutons DBCC CHECKCONSTRAINTS
pour le tableau. Pour mieux montrer l'impact, j'ai exécuté le script Create Enlarged AdventureWorks Tables.sql de Jonathan Kehayias (blog | @SQLPoolBoy) pour créer des tables plus grandes. Le script de Jonathan ne crée que les index des tables. Les instructions ci-dessous sont donc nécessaires pour ajouter quelques contraintes sélectionnées :
USE [AdventureWorks2012]; GO ALTER TABLE [Sales].[SalesOrderDetailEnlarged] WITH CHECK ADD CONSTRAINT [FK_SalesOrderDetailEnlarged_SalesOrderHeaderEnlarged_SalesOrderID] FOREIGN KEY([SalesOrderID]) REFERENCES [Sales].[SalesOrderHeaderEnlarged] ([SalesOrderID]) ON DELETE CASCADE; GO ALTER TABLE [Sales].[SalesOrderDetailEnlarged] WITH CHECK ADD CONSTRAINT [CK_SalesOrderDetailEnlarged_OrderQty] CHECK (([OrderQty]>(0))) GO ALTER TABLE [Sales].[SalesOrderDetailEnlarged] WITH CHECK ADD CONSTRAINT [CK_SalesOrderDetailEnlarged_UnitPrice] CHECK (([UnitPrice]>=(0.00))); GO ALTER TABLE [Sales].[SalesOrderHeaderEnlarged] WITH CHECK ADD CONSTRAINT [CK_SalesOrderHeaderEnlarged_DueDate] CHECK (([DueDate]>=[OrderDate])) GO ALTER TABLE [Sales].[SalesOrderHeaderEnlarged] WITH CHECK ADD CONSTRAINT [CK_SalesOrderHeaderEnlarged_Freight] CHECK (([Freight]>=(0.00))) GO
Nous pouvons vérifier quelles contraintes existent en utilisant sp_helpconstraint
:
EXEC sp_helpconstraint '[Sales].[SalesOrderDetailEnlarged]'; GO
résultat de sp_helpconstraint
Une fois que les contraintes existent, nous pouvons comparer l'utilisation des ressources pour DBCC CHECKCONSTRAINTS
pour une seule contrainte, une table et la base de données entière à l'aide d'événements étendus. Nous allons d'abord créer une session qui capture simplement sp_statement_completed
événements, inclut le sql_text
action et envoie la sortie au ring_buffer
:
CREATE EVENT SESSION [Constraint_Performance] ON SERVER ADD EVENT sqlserver.sp_statement_completed ( ACTION(sqlserver.database_id,sqlserver.sql_text) ) ADD TARGET package0.ring_buffer ( SET max_events_limit=(5000) ) WITH ( MAX_MEMORY=32768 KB, EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS, MAX_DISPATCH_LATENCY=30 SECONDS, MAX_EVENT_SIZE=0 KB, MEMORY_PARTITION_MODE=NONE, TRACK_CAUSALITY=OFF, STARTUP_STATE=OFF ); GO
Ensuite, nous allons démarrer la session et exécuter chacune des DBCC CHECKCONSTRAINT
commandes, puis affichez le tampon circulaire dans une table temporaire à manipuler. Notez que DBCC DROPCLEANBUFFERS
s'exécute avant chaque vérification afin que chacune démarre à partir du cache à froid, en conservant un champ de test de niveau.
ALTER EVENT SESSION [Constraint_Performance] ON SERVER STATE=START; GO USE [AdventureWorks2012]; GO DBCC DROPCLEANBUFFERS; GO DBCC CHECKCONSTRAINTS ('[Sales].[CK_SalesOrderDetailEnlarged_OrderQty]') WITH NO_INFOMSGS; GO DBCC DROPCLEANBUFFERS; GO DBCC CHECKCONSTRAINTS ('[Sales].[FK_SalesOrderDetailEnlarged_SalesOrderHeaderEnlarged_SalesOrderID]') WITH NO_INFOMSGS; GO DBCC DROPCLEANBUFFERS; GO DBCC CHECKCONSTRAINTS ('[Sales].[SalesOrderDetailEnlarged]') WITH NO_INFOMSGS; GO DBCC DROPCLEANBUFFERS; GO DBCC CHECKCONSTRAINTS WITH ALL_CONSTRAINTS, NO_INFOMSGS; GO DECLARE @target_data XML; SELECT @target_data = CAST(target_data AS XML) FROM sys.dm_xe_sessions AS s INNER JOIN sys.dm_xe_session_targets AS t ON t.event_session_address = s.[address] WHERE s.name = N'Constraint_Performance' AND t.target_name = N'ring_buffer'; SELECT n.value('(@name)[1]', 'varchar(50)') AS event_name, DATEADD(HOUR ,DATEDIFF(HOUR, SYSUTCDATETIME(), SYSDATETIME()),n.value('(@timestamp)[1]', 'datetime2')) AS [timestamp], n.value('(data[@name="duration"]/value)[1]', 'bigint') AS duration, n.value('(data[@name="physical_reads"]/value)[1]', 'bigint') AS physical_reads, n.value('(data[@name="logical_reads"]/value)[1]', 'bigint') AS logical_reads, n.value('(action[@name="sql_text"]/value)[1]', 'varchar(max)') AS sql_text, n.value('(data[@name="statement"]/value)[1]', 'varchar(max)') AS [statement] INTO #EventData FROM @target_data.nodes('RingBufferTarget/event[@name=''sp_statement_completed'']') AS q(n); GO ALTER EVENT SESSION [Constraint_Performance] ON SERVER STATE=STOP; GO
Analyser le ring_buffer
dans une table temporaire peut prendre un peu plus de temps (environ 20 secondes sur ma machine), mais l'interrogation répétée des données est plus rapide à partir d'une table temporaire que via le ring_buffer
. Si nous regardons la sortie, nous voyons qu'il y a plusieurs instructions exécutées pour chaque DBCC CHECKCONSTRAINTS
:
SELECT * FROM #EventData WHERE [sql_text] LIKE 'DBCC%';
Sortie d'événements étendus
Utilisation d'événements étendus pour approfondir le fonctionnement interne de CHECKCONSTRAINTS
est une tâche intéressante, mais ce qui nous intéresse vraiment ici, c'est la consommation de ressources - en particulier les E/S. Nous pouvons agréger les physical_reads
pour chaque commande de vérification pour comparer les E/S :
SELECT [sql_text], SUM([physical_reads]) AS [Total Reads] FROM #EventData WHERE [sql_text] LIKE 'DBCC%' GROUP BY [sql_text];
E/S agrégées pour les vérifications
Afin de vérifier une contrainte, SQL Server doit parcourir les données pour trouver toutes les lignes susceptibles de violer la contrainte. La définition du CK_SalesOrderDetailEnlarged_OrderQty
la contrainte est [OrderQty] > 0
. La contrainte de clé étrangère, FK_SalesOrderDetailEnlarged_SalesOrderHeaderEnlarged_SalesOrderID
, établit une relation sur SalesOrderID
entre le [Sales].[SalesOrderHeaderEnlarged]
et [Sales].[SalesOrderDetailEnlarged]
les tables. Intuitivement, il peut sembler que la vérification de la contrainte de clé étrangère nécessiterait plus d'E/S, car SQL Server doit lire les données de deux tables. Cependant, [SalesOrderID]
existe au niveau feuille du IX_SalesOrderHeaderEnlarged_SalesPersonID
index non clusterisé sur [Sales].[SalesOrderHeaderEnlarged]
table, et dans le IX_SalesOrderDetailEnlarged_ProductID
index sur le [Sales].[SalesOrderDetailEnlarged]
table. En tant que tel, SQL Server analyse ces deux index pour comparer le [SalesOrderID]
valeurs entre les deux tables. Cela nécessite un peu plus de 19 000 lectures. Dans le cas de CK_SalesOrderDetailEnlarged_OrderQty
contrainte, la [OrderQty]
la colonne n'est incluse dans aucun index, donc une analyse complète de l'index clusterisé se produit, ce qui nécessite plus de 72 000 lectures.
Lorsque toutes les contraintes d'une table sont vérifiées, les exigences d'E/S sont plus élevées que si une seule contrainte est vérifiée, et elles augmentent à nouveau lorsque la base de données entière est vérifiée. Dans l'exemple ci-dessus, le [Sales].[SalesOrderHeaderEnlarged]
et [Sales].[SalesOrderDetailEnlarged]
les tables sont disproportionnellement plus grandes que les autres tables de la base de données. Ce n'est pas rare dans les scénarios du monde réel; très souvent, les bases de données ont plusieurs grandes tables qui constituent une grande partie de la base de données. Lors de l'exécution de CHECKCONSTRAINTS
pour ces tables, tenez compte de la consommation potentielle de ressources requises pour la vérification. Effectuez des vérifications pendant les heures creuses lorsque cela est possible pour minimiser l'impact sur l'utilisateur. Dans le cas où les vérifications doivent être exécutées pendant les heures normales de bureau, comprendre quelles contraintes existent et quels index existent pour prendre en charge la validation peut aider à évaluer l'effet de la vérification. Vous pouvez d'abord exécuter des vérifications dans un environnement de test ou de développement pour comprendre l'impact sur les performances, mais des variations peuvent ensuite exister en fonction du matériel, des données comparables, etc. Et enfin, n'oubliez pas que chaque fois que vous exécutez une commande de vérification qui inclut le REPAIR_ALLOW_DATA_LOSS
option, suivez la réparation avec DBCC CHECKCONSTRAINTS
. La réparation de la base de données ne prend en compte aucune contrainte car la corruption est corrigée, donc en plus de perdre potentiellement des données, vous pouvez vous retrouver avec des données qui violent une ou plusieurs contraintes dans votre base de données.