Database
 sql >> Datenbank >  >> RDS >> Database

Ein Blick auf DBCC CHECKCONSTRAINTS und I/O

Ein allgemeines Element, das im Datenbankdesign verwendet wird, ist die Einschränkung. Constraints gibt es in einer Vielzahl von Varianten (z. B. default, unique) und erzwingen die Integrität der Spalte(n), auf der/denen sie vorhanden sind. Bei guter Implementierung sind Einschränkungen eine leistungsstarke Komponente beim Design einer Datenbank, da sie verhindern, dass Daten, die bestimmte Kriterien nicht erfüllen, in eine Datenbank gelangen. Einschränkungen können jedoch mit Befehlen wie WITH NOCHECK verletzt werden und IGNORE_CONSTRAINTS . Außerdem bei Verwendung des REPAIR_ALLOW_DATA_LOSS Option mit jedem DBCC CHECK Befehl zum Reparieren einer Datenbankbeschädigung, Beschränkungen werden nicht berücksichtigt.

Folglich ist es möglich, ungültige Daten in der Datenbank zu haben – entweder Daten, die sich nicht an eine Einschränkung halten, oder Daten, die nicht mehr die erwartete Primär-Fremdschlüssel-Beziehung aufrechterhalten. SQL Server enthält die DBCC CHECKCONSTRAINTS -Anweisung, um Daten zu finden, die Einschränkungen verletzen. Führen Sie nach Ausführung einer beliebigen Reparaturoption DBCC CHECKCONSTRAINTS aus für die gesamte Datenbank, um sicherzustellen, dass es keine Probleme gibt, und es kann Zeiten geben, in denen es angebracht ist, CHECKCONSTRAINTS auszuführen für eine Select-Einschränkung oder eine Tabelle. Die Aufrechterhaltung der Datenintegrität ist von entscheidender Bedeutung, und obwohl es nicht typisch ist, DBCC CHECKCONSTRAINTS auszuführen geplant, um ungültige Daten zu finden, wenn Sie es ausführen müssen, ist es eine gute Idee, die Auswirkungen auf die Leistung zu verstehen, die es verursachen kann.

DBCC-CHECKCONSTRAINTS kann für eine einzelne Einschränkung, eine Tabelle oder die gesamte Datenbank ausgeführt werden. Wie bei anderen Prüfbefehlen kann die Ausführung viel Zeit in Anspruch nehmen und Systemressourcen beanspruchen, insbesondere bei größeren Datenbanken. Im Gegensatz zu anderen Prüfbefehlen, CHECKCONSTRAINTS verwendet keinen Datenbank-Snapshot.

Mit erweiterten Ereignissen können wir die Ressourcennutzung untersuchen, wenn wir DBCC CHECKCONSTRAINTS ausführen für den Tisch. Um die Auswirkungen besser zu zeigen, habe ich das Skript Create Enlarged AdventureWorks Tables.sql von Jonathan Kehayias (Blog | @SQLPoolBoy) ausgeführt, um größere Tabellen zu erstellen. Jonathans Skript erstellt nur die Indizes für die Tabellen, daher sind die folgenden Anweisungen erforderlich, um einige ausgewählte Einschränkungen hinzuzufügen:

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

Mit sp_helpconstraint können wir überprüfen, welche Beschränkungen bestehen :

EXEC sp_helpconstraint '[Sales].[SalesOrderDetailEnlarged]';
GO


sp_helpconstraint-Ausgabe

Sobald die Einschränkungen bestehen, können wir die Ressourcennutzung für DBCC CHECKCONSTRAINTS vergleichen für eine einzelne Einschränkung, eine Tabelle und die gesamte Datenbank mithilfe von Extended Events. Zuerst erstellen wir eine Sitzung, die einfach sp_statement_completed erfasst Ereignisse, enthält den sql_text Aktion und sendet die Ausgabe an den 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

Als Nächstes starten wir die Sitzung und führen alle DBCC CHECKCONSTRAINT aus Befehle und geben Sie dann den Ringpuffer an eine temporäre Tabelle aus, um ihn zu bearbeiten. Beachten Sie, dass DBCC DROPCLEANBUFFERS wird vor jeder Prüfung ausgeführt, sodass jede vom Cold-Cache startet und ein Testfeld auf Level hält.

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

Analysieren des ring_buffer in eine temporäre Tabelle kann etwas länger dauern (ca. 20 Sekunden auf meiner Maschine), aber das wiederholte Abfragen der Daten aus einer temporären Tabelle ist schneller als über den ring_buffer . Wenn wir uns die Ausgabe ansehen, sehen wir, dass mehrere Anweisungen für jeden DBCC CHECKCONSTRAINTS ausgeführt werden :

SELECT *
FROM #EventData
WHERE [sql_text] LIKE 'DBCC%';


Erweiterte Ereignisausgabe

Erweiterte Ereignisse verwenden, um in die inneren Abläufe von CHECKCONSTRAINTS einzutauchen ist eine interessante Aufgabe, aber was uns hier wirklich interessiert, ist der Ressourcenverbrauch – insbesondere I/O. Wir können die physical_reads aggregieren für jeden Prüfbefehl zum Vergleichen der E/A:

SELECT [sql_text], SUM([physical_reads]) AS [Total Reads]
FROM #EventData
WHERE [sql_text] LIKE 'DBCC%'
GROUP BY [sql_text];


Aggregierte E/A für Prüfungen

Um eine Einschränkung zu überprüfen, muss SQL Server die Daten durchlesen, um alle Zeilen zu finden, die möglicherweise gegen die Einschränkung verstoßen. Die Definition von CK_SalesOrderDetailEnlarged_OrderQty Einschränkung ist [OrderQty]> 0 . Die Fremdschlüsseleinschränkung FK_SalesOrderDetailEnlarged_SalesOrderHeaderEnlarged_SalesOrderID , stellt eine Beziehung auf SalesOrderID her zwischen [Sales].[SalesOrderHeaderEnlarged] und [Sales].[SalesOrderDetailEnlarged] Tische. Intuitiv mag es scheinen, als würde die Überprüfung der Fremdschlüsseleinschränkung mehr E/A erfordern, da SQL Server Daten aus zwei Tabellen lesen muss. Jedoch [SalesOrderID] existiert in der Blattebene von IX_SalesOrderHeaderEnlarged_SalesPersonID Nonclustered-Index für [Sales].[SalesOrderHeaderEnlarged] Tabelle und in der IX_SalesOrderDetailEnlarged_ProductID Index auf [Sales].[SalesOrderDetailEnlarged] Tisch. Daher scannt SQL Server diese beiden Indizes, um die [SalesOrderID] zu vergleichen Werte zwischen den beiden Tabellen. Dies erfordert etwas mehr als 19.000 Lesevorgänge. Im Fall von CK_SalesOrderDetailEnlarged_OrderQty Beschränkung, die [OrderQty] -Spalte ist in keinem Index enthalten, sodass ein vollständiger Scan des Clustered-Index erfolgt, der über 72.000 Lesevorgänge erfordert.

Wenn alle Constraints für eine Tabelle geprüft werden, sind die I/O-Anforderungen höher als wenn ein einzelnes Constraint geprüft wird, und sie steigen wieder, wenn die gesamte Datenbank geprüft wird. Im obigen Beispiel der [Sales].[SalesOrderHeaderEnlarged] und [Sales].[SalesOrderDetailEnlarged] Tabellen sind unverhältnismäßig größer als andere Tabellen in der Datenbank. Dies ist in realen Szenarien nicht ungewöhnlich; Sehr oft haben Datenbanken mehrere große Tabellen, die einen großen Teil der Datenbank umfassen. Beim Ausführen von CHECKCONSTRAINTS Beachten Sie bei diesen Tabellen den potenziellen Ressourcenverbrauch, der für die Prüfung erforderlich ist. Führen Sie Prüfungen nach Möglichkeit außerhalb der Geschäftszeiten durch, um die Auswirkungen auf die Benutzer zu minimieren. Für den Fall, dass Prüfungen während der normalen Geschäftszeiten ausgeführt werden müssen, kann es hilfreich sein, die Auswirkungen der Prüfung abzuschätzen, wenn Sie wissen, welche Einschränkungen bestehen und welche Indizes zur Unterstützung der Validierung vorhanden sind. Sie können zunächst Prüfungen in einer Test- oder Entwicklungsumgebung durchführen, um die Auswirkungen auf die Leistung zu verstehen, aber dann können aufgrund von Hardware, vergleichbaren Daten usw Option, folgen Sie der Reparatur mit DBCC CHECKCONSTRAINTS . Bei der Datenbankreparatur werden keine Beschränkungen berücksichtigt, da die Beschädigung behoben wird, sodass Sie möglicherweise nicht nur Daten verlieren, sondern auch Daten erhalten, die gegen eine oder mehrere Beschränkungen in Ihrer Datenbank verstoßen.