sql >> Database teknologi >  >> RDS >> Database

Et kig på DBCC CHECKCONSTRAINTS og I/O

Et almindeligt element, der bruges i databasedesign, er begrænsningen. Begrænsninger kommer i en række forskellige varianter (f.eks. standard, unikke) og håndhæver integriteten af ​​den eller de kolonner, som de findes på. Når de implementeres godt, er begrænsninger en kraftfuld komponent i designet af en database, fordi de forhindrer data, der ikke opfylder fastsatte kriterier, i at komme ind i en database. Begrænsninger kan dog overtrædes ved hjælp af kommandoer såsom WITH NOCHECK og IGNORE_CONSTRAINTS . Derudover, når du bruger REPAIR_ALLOW_DATA_LOSS mulighed med en hvilken som helst DBCC CHECK kommando til at reparere databasekorruption, tages der ikke hensyn til begrænsninger.

Det er derfor muligt at have ugyldige data i databasen – enten data, der ikke overholder en begrænsning, eller data, der ikke længere opretholder den forventede primær-fremmednøgle relation. SQL Server inkluderer DBCC CHECKCONSTRAINTS erklæring for at finde data, der overtræder begrænsninger. Når enhver reparationsindstilling er udført, skal du køre DBCC CHECKCONSTRAINTS for hele databasen for at sikre, at der ikke er problemer, og der kan være tidspunkter, hvor det er passende at køre CHECKCONSTRAINTS for en udvalgt begrænsning eller en tabel. Opretholdelse af dataintegritet er kritisk, og selvom det ikke er typisk at køre DBCC CHECKCONSTRAINTS på en planlagt basis for at finde ugyldige data, når du skal køre dem, er det en god idé at forstå den præstationspåvirkning, det kan skabe.

DBCC CHECKCONSTRAINTS kan udføres for en enkelt begrænsning, en tabel eller hele databasen. Ligesom andre kontrolkommandoer kan det tage betydelig tid at fuldføre og vil forbruge systemressourcer, især for større databaser. I modsætning til andre kontrolkommandoer, CHECKCONSTRAINTS bruger ikke et øjebliksbillede af databasen.

Med Extended Events kan vi undersøge ressourceforbrug, når vi udfører DBCC CHECKCONSTRAINTS til bordet. For bedre at vise virkningen kørte jeg scriptet Create Enlarged AdventureWorks Tables.sql fra Jonathan Kehayias (blog | @SQLPoolBoy) for at skabe større tabeller. Jonathans script opretter kun indekserne for tabellerne, så udsagn nedenfor er nødvendige for at tilføje nogle få udvalgte begrænsninger:

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

Vi kan verificere hvilke begrænsninger der findes ved at bruge sp_helpconstraint :

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


sp_helpconstraint output

Når først begrænsningerne eksisterer, kan vi sammenligne ressourceforbruget for DBCC CHECKCONSTRAINTS for en enkelt begrænsning, en tabel og hele databasen ved hjælp af udvidede hændelser. Først opretter vi en session, der blot fanger sp_statement_completed begivenheder, inkluderer sql_text handling, og sender output til 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

Dernæst starter vi sessionen og kører hver af DBCC CHECKCONSTRAINT kommandoer, udlæs derefter ringbufferen til en midlertidig tabel for at manipulere. Bemærk, at DBCC DROPCLEANBUFFERS udføres før hver kontrol, så hver starter fra en kold cache og holder et testfelt på 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

Parser ring_buffer ind i en midlertidig tabel kan tage noget ekstra tid (ca. 20 sekunder på min maskine), men gentagen forespørgsel efter data er hurtigere fra en midlertidig tabel end via ring_buffer . Hvis vi ser på outputtet, ser vi, at der udføres flere sætninger for hver DBCC CHECKCONSTRAINTS :

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


Udvidet hændelsesoutput

Brug af udvidede hændelser til at grave ind i CHECKCONSTRAINTSs indre funktioner er en interessant opgave, men det, vi virkelig er interesseret i her, er ressourceforbrug – specifikt I/O. Vi kan aggregere physical_reads for hver kontrolkommando for at sammenligne I/O:

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


Aggregeret I/O til checks

For at kontrollere en begrænsning skal SQL Server læse dataene igennem for at finde rækker, der kan overtræde begrænsningen. Definitionen af ​​CK_SalesOrderDetailEnlarged_OrderQty begrænsning er [OrderQty] > 0 . Begrænsningen for fremmednøgle, FK_SalesOrderDetailEnlarged_SalesOrderHeaderEnlarged_SalesOrderID , etablerer en relation på SalesOrderID mellem [Sales].[SalesOrderHeaderEnlarged] og [Sales].[SalesOrderDetailEnlarged] tabeller. Intuitivt kan det virke som om at kontrollere den fremmede nøgle-begrænsning ville kræve mere I/O, da SQL Server skal læse data fra to tabeller. Dog [SalesOrderID] findes i bladniveauet af IX_SalesOrderHeaderEnlarged_SalesPersonID ikke-klyngede indeks på [Sales].[SalesOrderHeaderEnlarged] tabellen og i IX_SalesOrderDetailEnlarged_ProductID indeks på [Sales].[SalesOrderDetailEnlarged] bord. Som sådan scanner SQL Server disse to indekser for at sammenligne [SalesOrderID] værdier mellem de to tabeller. Dette kræver lidt over 19.000 læsninger. I tilfælde af CK_SalesOrderDetailEnlarged_OrderQty begrænsning, [OrderQty] kolonne er ikke inkluderet i noget indeks, så en fuld scanning af det klyngede indeks sker, hvilket kræver over 72.000 læsninger.

Når alle begrænsningerne for en tabel er kontrolleret, er I/O-kravene højere, end hvis en enkelt begrænsning kontrolleres, og de øges igen, når hele databasen kontrolleres. I eksemplet ovenfor er [Sales].[SalesOrderHeaderEnlarged] og [Sales].[SalesOrderDetailEnlarged] tabeller er uforholdsmæssigt større end andre tabeller i databasen. Dette er ikke ualmindeligt i scenarier i den virkelige verden; meget ofte har databaser flere store tabeller, som udgør en stor del af databasen. Når du kører CHECKCONSTRAINTS for disse tabeller skal du være opmærksom på det potentielle ressourceforbrug, der kræves til kontrollen. Kør tjek uden for timer, når det er muligt for at minimere brugerpåvirkningen. I tilfælde af, at tjek skal køre inden for normal arbejdstid, kan en forståelse af, hvilke begrænsninger der findes, og hvilke indekser der findes for at understøtte validering, hjælpe med at måle effekten af ​​kontrollen. Du kan udføre kontroller i et test- eller udviklingsmiljø først for at forstå virkningen af ​​ydeevnen, men variationer kan derefter eksistere baseret på hardware, sammenlignelige data osv.  Og husk endelig, at hver gang du kører en kontrolkommando, der inkluderer REPAIR_ALLOW_DATA_LOSS mulighed, følg reparationen med DBCC CHECKCONSTRAINTS . Databasereparation tager ingen begrænsninger i betragtning, da korruption er rettet, så ud over potentielt at miste data kan du ende med data, der overtræder en eller flere begrænsninger i din database.


  1. Forskellen i håndteringen af ​​mellemrummene mellem Oracle og SQL Server

  2. SQLite NOT NULL-begrænsning

  3. SQL ORDER BY klausul for begyndere

  4. Forespørgsel, der ignorerer mellemrummene