Běžným prvkem používaným při návrhu databáze je omezení. Omezení přicházejí v různých variantách (např. výchozí, jedinečné) a vynucují integritu sloupců, na kterých existují. Při správné implementaci jsou omezení mocnou součástí návrhu databáze, protože zabraňují tomu, aby se data, která nesplňují stanovená kritéria, dostala do databáze. Omezení však lze porušit pomocí příkazů jako WITH NOCHECK
a IGNORE_CONSTRAINTS
. Navíc při použití REPAIR_ALLOW_DATA_LOSS
možnost s libovolnou DBCC CHECK
příkaz k opravě poškození databáze, omezení se neberou v úvahu.
V důsledku toho je možné mít v databázi neplatná data – buď data, která nesplňují omezení, nebo data, která již neudržují očekávaný vztah primárního a cizího klíče. SQL Server obsahuje DBCC CHECKCONSTRAINTS
k nalezení dat, která porušují omezení. Po provedení jakékoli možnosti opravy spusťte DBCC CHECKCONSTRAINTS
pro celou databázi, aby se zajistilo, že neexistují žádné problémy, a mohou nastat situace, kdy je vhodné spustit CHECKCONSTRAINTS
pro výběrové omezení nebo tabulku. Udržování integrity dat je zásadní, a přestože není obvyklé spouštět DBCC CHECKCONSTRAINTS
na plánovaném základě najít neplatná data, když je potřebujete spustit, je dobré porozumět tomu, jaký dopad to může mít na výkon.
DBCC CHECKCONSTRAINTS
lze spustit pro jediné omezení, tabulku nebo celou databázi. Stejně jako ostatní kontrolní příkazy může jeho dokončení trvat značnou dobu a spotřebovává systémové prostředky, zejména u větších databází. Na rozdíl od jiných kontrolních příkazů CHECKCONSTRAINTS
nepoužívá snímek databáze.
Pomocí Extended Events můžeme zkoumat využití zdrojů, když spustíme DBCC CHECKCONSTRAINTS
pro stůl. Abych lépe ukázal dopad, spustil jsem skript Create Enlarged AdventureWorks Tables.sql od Jonathana Kehayiase (blog | @SQLPoolBoy) k vytvoření větších tabulek. Jonathanův skript vytváří pouze indexy pro tabulky, takže k přidání několika vybraných omezení jsou nutné následující příkazy:
Jaká omezení existují, můžeme ověřit pomocí sp_helpconstraint
:
EXEC sp_helpconstraint '[Sales].[SalesOrderDetailEnlarged]';GO
sp_helpconstraint výstup
Jakmile budou existovat omezení, můžeme porovnat využití zdrojů pro DBCC CHECKCONSTRAINTS
pro jediné omezení, tabulku a celou databázi pomocí Extended Events. Nejprve vytvoříme relaci, která jednoduše zachytí sp_statement_completed
události, zahrnuje sql_text
akci a odešle výstup do ring_buffer
:
VYTVOŘTE RELACI UDÁLOSTI [Constraint_Performance] NA SERVERADD UDÁLOSTI sqlserver.sp_statement_completed( ACTION(sqlserver.database_id,sqlserver.sql_text))ADD CÍLOVÝ balíček0.ring_buffer( SET max_events003) EVLO7=EVLO_limit=TEN_TEN , MAX_DISPATCH_LATENCY=30 SECONDS, MAX_EVENT_SIZE=0 kB, MEMORY_PARTITION_MODE=NONE, TRACK_CAUSALITY=OFF, STARTUP_STATE=OFF);GO
Dále zahájíme relaci a spustíme každý z DBCC CHECKCONSTRAINT
příkazy, poté vypište kruhovou vyrovnávací paměť do dočasné tabulky, abyste mohli manipulovat. Všimněte si, že DBCC DROPCLEANBUFFERS
provede se před každou kontrolou, takže každá začne z chladné mezipaměti, přičemž se zachová pole pro testování úrovně.
ALTER EVENT SESSION [Constraint_Performance]ON SERVERSTATE=START;POUŽÍVEJTE [AdventureWorks2012];GO DBCC DROPCLEANBUFFERS;GODBCC CHECKCONSTRAINTS ('[Prodej].[CONSTRAINT_SalesOrderDetail]FFC . [Fk_salesorderdetailenlarged_salesorderheaderenlarged_salesorderid] ') s no_infomsgs; GodBCC DropCleanBuffers; GodBCC checkconstraints (' [SalesorderDetailenlarged] ') s no_informsg; 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_Performance' AND N'tarfferget_name_t. '; SELECT n.value('(@name)[1]', 'varchar(50)') AS název_události, DATEADD(HOUR ,DATEDIFF(HOUR, SYSUTCDATETIME(), SYSDATETIME()),n.value('(@timestamp) )[1]', 'datetime2')) AS [časové razítko], n.value('(data[@name="duration"]/value)[1]', 'bigint') AS trvání, n.value( '(data[@name="physical_reads"]/value)[1]', 'bigint') AS fyzické_čtení, n.value('(data[@name="logic_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 [příkaz]DO #EventDataFROM @target_data.nodes('RingBufferTarget/event[@name=''sp_statement_completed'']') AS q( n);PŘEJÍT ZMĚNIT RELACI UDÁLOSTI [Constraint_Performance]NA SERVERSTATE=STOP;GO
Analýza ring_buffer
do dočasné tabulky může nějakou dobu trvat (na mém počítači asi 20 sekund), ale opakované dotazování na data je rychlejší z dočasné tabulky než přes ring_buffer
. Pokud se podíváme na výstup, vidíme, že pro každý DBCC CHECKCONSTRAINTS
je provedeno několik příkazů :
SELECT *FROM #EventDataWHERE [sql_text] LIKE 'DBCC%';
Výstup rozšířených událostí
Použití Extended Events k prozkoumání vnitřního fungování CHECKCONSTRAINTS
je zajímavý úkol, ale to, co nás opravdu zajímá, je spotřeba zdrojů – konkrétně I/O. Můžeme agregovat physical_reads
pro každý kontrolní příkaz k porovnání I/O:
SELECT [sql_text], SUM([physical_reads]) AS [Total Reads]FROM #EventDataWHERE [sql_text] LIKE 'DBCC%'GROUP BY [sql_text];
Agregované I/O pro kontroly
Aby bylo možné zkontrolovat omezení, musí SQL Server přečíst data, aby našel všechny řádky, které by mohly omezení porušovat. Definice CK_SalesOrderDetailEnlarged_OrderQty
omezení je [OrderQty] > 0
. Omezení cizího klíče, FK_SalesOrderDetailEnlarged_SalesOrderHeaderEnlarged_SalesOrderID
, naváže vztah na SalesOrderID
mezi [Sales].[SalesOrderHeaderEnlarged]
a [Sales].[SalesOrderDetailEnlarged]
tabulky. Intuitivně by se mohlo zdát, že kontrola omezení cizího klíče by vyžadovala více I/O, protože SQL Server musí číst data ze dvou tabulek. Nicméně [SalesOrderID]
existuje na úrovni listu IX_SalesOrderHeaderEnlarged_SalesPersonID
nonclustered index na [Sales].[SalesOrderHeaderEnlarged]
tabulce a v IX_SalesOrderDetailEnlarged_ProductID
index na [Sales].[SalesOrderDetailEnlarged]
stůl. SQL Server jako takový prohledá tyto dva indexy, aby porovnal [SalesOrderID]
hodnoty mezi dvěma tabulkami. To vyžaduje něco málo přes 19 000 přečtení. V případě CK_SalesOrderDetailEnlarged_OrderQty
omezení, [OrderQty]
sloupec není zahrnut v žádném indexu, takže dojde k úplnému prohledání seskupeného indexu, což vyžaduje více než 72 000 přečtení.
Když jsou zaškrtnuta všechna omezení pro tabulku, požadavky na vstup/výstup jsou vyšší, než když je kontrolováno jediné omezení, a znovu se zvýší, když se kontroluje celá databáze. Ve výše uvedeném příkladu [Sales].[SalesOrderHeaderEnlarged]
a [Sales].[SalesOrderDetailEnlarged]
tabulky jsou neúměrně větší než jiné tabulky v databázi. To není neobvyklé ve scénářích reálného světa; databáze mají velmi často několik velkých tabulek, které tvoří velkou část databáze. Při spuštění CHECKCONSTRAINTS
u těchto tabulek si uvědomte potenciální spotřebu zdrojů potřebnou pro kontrolu. Pokud je to možné, provádějte kontroly mimo pracovní dobu, abyste minimalizovali dopad na uživatele. V případě, že kontroly musí probíhat během běžné pracovní doby, pochopení toho, jaká existují omezení a jaké existují indexy pro podporu ověřování, může pomoci změřit účinek kontroly. Nejprve můžete provést kontroly v testovacím nebo vývojovém prostředí, abyste pochopili dopad na výkon, ale poté mohou existovat odchylky na základě hardwaru, srovnatelných dat atd. A nakonec nezapomeňte, že kdykoli spustíte kontrolní příkaz, který obsahuje REPAIR_ALLOW_DATA_LOSS
možnost, pokračujte v opravě pomocí DBCC CHECKCONSTRAINTS
. Oprava databáze nebere v úvahu žádná omezení, protože poškození je opraveno, takže kromě potenciální ztráty dat můžete skončit s daty, která porušují jedno nebo více omezení ve vaší databázi.