sql >> Databasteknik >  >> RDS >> Database

En titt på DBCC CHECKCONSTRAINTS och I/O

Ett vanligt element som används i databasdesign är begränsningen. Begränsningar finns i en mängd olika smaker (t.ex. standard, unik) och upprätthåller integriteten hos kolumnen/kolumnerna där de finns. När de implementeras väl är begränsningar en kraftfull komponent i designen av en databas eftersom de förhindrar att data som inte uppfyller fastställda kriterier kommer in i en databas. Däremot kan begränsningar överträdas med kommandon som WITH NOCHECK och IGNORE_CONSTRAINTS . Dessutom, när du använder REPAIR_ALLOW_DATA_LOSS alternativ med valfri DBCC CHECK kommando för att reparera databaskorruption, beaktas inte begränsningar.

Följaktligen är det möjligt att ha ogiltiga data i databasen – antingen data som inte följer en begränsning eller data som inte längre upprätthåller den förväntade primär-främmande nyckelrelationen. SQL Server inkluderar DBCC CHECKCONSTRAINTS uttalande för att hitta data som bryter mot begränsningar. När något reparationsalternativ har körts, kör DBCC CHECKCONSTRAINTS för hela databasen för att säkerställa att det inte finns några problem, och det kan finnas tillfällen då det är lämpligt att köra CHECKCONSTRAINTS för en vald begränsning eller en tabell. Att upprätthålla dataintegritet är avgörande, och även om det inte är vanligt att köra DBCC CHECKCONSTRAINTS på en schemalagd basis för att hitta ogiltiga data, när du behöver köra den är det en bra idé att förstå vilken resultateffekt det kan skapa.

DBCC CHECKCONSTRAINTS kan köras för en enda begränsning, en tabell eller hela databasen. Liksom andra kontrollkommandon kan det ta avsevärd tid att slutföra och kommer att förbruka systemresurser, särskilt för större databaser. Till skillnad från andra kontrollkommandon, CHECKCONSTRAINTS använder inte en ögonblicksbild av databasen.

Med Extended Events kan vi undersöka resursanvändning när vi kör DBCC CHECKCONSTRAINTS för bordet. För att bättre visa effekten körde jag skriptet Create Enlarged AdventureWorks Tables.sql från Jonathan Kehayias (blogg | @SQLPoolBoy) för att skapa större tabeller. Jonathans skript skapar bara indexen för tabellerna, så påståendena nedan är nödvändiga för att lägga till några utvalda begränsningar:

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 verifiera vilka begränsningar som finns med sp_helpconstraint :

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


sp_helpconstraint output

När begränsningarna väl finns kan vi jämföra resursanvändningen för DBCC CHECKCONSTRAINTS för en enda begränsning, en tabell och hela databasen med hjälp av utökade händelser. Först skapar vi en session som helt enkelt fångar sp_statement_completed händelser, inkluderar sql_text action och skickar utdata till 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

Därefter startar vi sessionen och kör var och en av DBCC CHECKCONSTRAINT kommandon, mata sedan ut ringbufferten till en temporär tabell för att manipulera. Observera att DBCC DROPCLEANBUFFERS körs före varje kontroll så att var och en startar från kall cache, med ett nivåtestfä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

Parsar ring_buffer in i en temporär tabell kan det ta lite extra tid (cirka 20 sekunder på min maskin), men upprepad sökning av data är snabbare från en temporär tabell än via ring_buffer . Om vi ​​tittar på utdata ser vi att det finns flera programsatser som körs för varje DBCC CHECKCONSTRAINTS :

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


Utgång för utökade händelser

Använda utökade händelser för att gräva in i CHECKCONSTRAINTSs inre funktioner är en intressant uppgift, men det vi verkligen är intresserade av här är resursförbrukning – speciellt I/O. Vi kan aggregera physical_reads för varje kontrollkommando för att jämföra I/O:

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


Aggregerad I/O för kontroller

För att kontrollera en begränsning måste SQL Server läsa igenom data för att hitta några rader som kan bryta mot begränsningen. Definitionen av CK_SalesOrderDetailEnlarged_OrderQty begränsning är [OrderQty] > 0 . Den främmande nyckelbegränsningen, FK_SalesOrderDetailEnlarged_SalesOrderHeaderEnlarged_SalesOrderID , upprättar en relation på SalesOrderID mellan [Sales].[SalesOrderHeaderEnlarged] och [Sales].[SalesOrderDetailEnlarged] tabeller. Intuitivt kan det tyckas som om kontroll av den främmande nyckeln skulle kräva mer I/O, eftersom SQL Server måste läsa data från två tabeller. Men [SalesOrderID] finns i bladnivån för IX_SalesOrderHeaderEnlarged_SalesPersonID icke-klustrade index på [Sales].[SalesOrderHeaderEnlarged] tabellen och i IX_SalesOrderDetailEnlarged_ProductID index på [Sales].[SalesOrderDetailEnlarged] tabell. Som sådan skannar SQL Server dessa två index för att jämföra [SalesOrderID] värden mellan de två tabellerna. Detta kräver drygt 19 000 läsningar. I fallet med CK_SalesOrderDetailEnlarged_OrderQty begränsning, [OrderQty] kolumnen ingår inte i något index, så en fullständig genomsökning av det klustrade indexet sker, vilket kräver över 72 000 läsningar.

När alla begränsningar för en tabell är kontrollerade är I/O-kraven högre än om en enskild begränsning kontrolleras, och de ökar igen när hela databasen kontrolleras. I exemplet ovan visas [Sales].[SalesOrderHeaderEnlarged] och [Sales].[SalesOrderDetailEnlarged] tabeller är oproportionerligt större än andra tabeller i databasen. Detta är inte ovanligt i verkliga scenarier; mycket ofta har databaser flera stora tabeller som utgör en stor del av databasen. När du kör CHECKCONSTRAINTS för dessa tabeller, var medveten om den potentiella resursförbrukning som krävs för kontrollen. Kör kontroller under öppettider när det är möjligt för att minimera användarens påverkan. I händelse av att kontroller måste köras under normal kontorstid, kan förståelse av vilka begränsningar som finns och vilka index som finns för att stödja validering, hjälpa till att mäta effekten av kontrollen. Du kan utföra kontroller i en test- eller utvecklingsmiljö först för att förstå prestandapåverkan, men variationer kan sedan finnas baserat på hårdvara, jämförbara data, etc.  Och slutligen, kom ihåg att varje gång du kör ett kontrollkommando som innehåller REPAIR_ALLOW_DATA_LOSS alternativ, följ reparationen med DBCC CHECKCONSTRAINTS . Databasreparation tar inte hänsyn till några begränsningar eftersom korruption är åtgärdad, så förutom att potentiellt förlora data kan du sluta med data som bryter mot en eller flera begränsningar i din databas.


  1. Hur man får sista raden per grupp i PostgreSQL

  2. Cloud Vendor Deep-Dive:PostgreSQL på DigitalOcean

  3. Vad är det snabbaste sättet att bulka infoga mycket data i SQL Server (C#-klient)

  4. SQL Server 2008 Rad Infoga och uppdatera tidsstämplar