Ve svém předchozím příspěvku jsem prozkoumal různé metody sledování automatických aktualizací statistik, abych zjistil, zda ovlivňují výkon dotazu. Ve druhé polovině příspěvku jsem zahrnul možnosti, z nichž jednou bylo povolit nastavení databáze Auto Update Statistics Asynchronously. V tomto příspěvku se chci podívat na to, jak se změní výkon dotazu, když k automatické aktualizaci dojde před spuštěním dotazu, a co se stane s výkonem, pokud je aktualizace asynchronní.
Nastavení
Začal jsem s kopií databáze AdventureWorks2012 a poté jsem pomocí tohoto skriptu vytvořil kopii tabulky SalesOrderHeader s více než 200 miliony řádků. Tabulka má seskupený index na SalesOrderID a neclusterovaný index na CustomerID, OrderDate, SubTotal. [Poznámka:pokud budete provádět opakované testy, udělejte si v tuto chvíli zálohu této databáze, abyste si ušetřili čas]. Po načtení dat a vytvoření neshlukovaného indexu jsem ověřil počet řádků a vypočítal, kolik řádků (přibližně) by bylo potřeba upravit, aby se vyvolala automatická aktualizace.
SELECTOBJECT_NAME([p].[id_objektu]) [TableName],[si].[name] [IndexName],[au].[type_desc] [Type],[p].[rowrows] [RowCount], ([p].[rows]*.20) + 500 [UpdateThreshold],[au].total_pages [PageCount],(([au].[total_pages]*8)/1024)/1024 [TotalGB]FROM [sys ][oddíly] [p]PŘIPOJTE SE [sys].[alokační_jednotky] [au] ON [p].[id_partition] =[au].[id_kontejneru] PŘIPOJTE SE [sys].[indexy] [si] dne [p] .[id_objektu] =[si].id_objektu a [p].[id_indexu] =[si].[id_indexu]KDE [p].[id_objektu] =ID_OBJEKTU(N'Sales.Big_SalesOrderHeader');
Big_SalesOrderHeader Informace CIX a NCI
Také jsem ověřil aktuální záhlaví statistiky pro index:
DBCC SHOW_STATISTICS ('Sales.Big_SalesOrderHeader',[IX_Big_SalesOrderHeader_CustomerID_OrderDate_SubTotal]);
Statistiky NCI:Na začátku
Poté jsem vytvořil uloženou proceduru, kterou bych použil pro testování. Je to přímočarý postup, který se dotazuje Sales.Big_SalesOrderHeader a agreguje údaje o prodeji podle CustomerID a OrderDate pro analýzu:
CREATE PROCEDURE Sales.usp_GetCustomerStats@CustomerID INT,@StartDate DATETIME,@EndDate DATETIMEASBEGIN SET NOCOUNT ON; SELECT CustomerID, DATEPART(YEAR, OrderDate), DATEPART (MONTH, OrderDate), COUNT([SalesOrderID]) as Computed FROM [Sales].[Big_SalesOrderHeader] WHERE CustomerID =@CustomerID A OrderDate BETWEEN @StartDate a @BY CustomerDate,GROUP DATEPART(YEAR, OrderDate), DATEPART(MONTH, OrderDate) ORDER BY DATEPART (YEAR, OrderDate), DATEPART(MONTH, OrderDate);END
Nakonec jsem před provedením uložené procedury vytvořil relaci Extended Events, abych mohl sledovat trvání dotazu pomocí sp_statement_starting a sp_statement_completed. Přidal jsem také událost auto_stats, protože i když jsem neočekával, že dojde k aktualizaci, chtěl jsem později použít stejnou definici relace.
VYTVOŘIT RELACI UDÁLOSTI [StatsUpdate_QueryPerf]NA SERVERADD UDÁLOSTI sqlserver.auto_stats,PŘIDAT UDÁLOST sqlserver.sp_statement_completed(SET collect_statement=(1)),PŘIDAT UDÁLOST sqlserver.sp_statement_startingfile_souboru_souboru\TARGETventingADD. StatsUpdate_QueryPerf.xel')WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=30 SEKUND,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_CASTATON=OFF,UPTRACK_MODE=VYPNUTO);Test
Spustil jsem relaci Extended Events a poté jsem několikrát provedl uloženou proceduru s použitím různých CustomerID:
ALTER EVENT SESSION [StatsUpdate_QueryPerf]NA SERVERSTATE =START;GO EXEC Sales.usp_GetCustomerStats 11331, '2012-08-01 00:00:00.000', '2012-038-371:92 Sales'2012-038-371:92 11330, '2013-01-01 00:00:00.000', '2013-01-31 23:59:59.997'GOEXEC Sales.usp_GetCustomerStats 11506, '2012-11-01:01.01.01'2012-11-01:01 -30 23:59:59.997'GOEXEC Sales.usp_GetCustomerStats 17061, '2013-01-01 00:00:00.000', '2013-01-31 23:59:59.997'GOustomerStats.01 GOust-Customer Sales. 01 00:00:00.000', '2013-03-31 23:59:59.997'GOEXEC Sales.usp_GetCustomerStats 15131, '2013-02-01 00:00:00.0013', 29.29.2013 'GOEXEC Sales.usp_GetCustomerStats 29837, '2012-10-01 00:00:00.000', '2012-10-31 23:59:59.997'GOEXEC Sales.usp_GetCustomerStats,1'0005 1'0073 1'005 1'02 , '2013-03-31 23:59:59.997'GOOvěřil jsem počet provedení a plán dotazem na mezipaměť procedur:
SELECTOBJECT_NAME([st].[objectid]),[st].[text],[qs].[execution_count],[qs].[creation_time],[qs].[last_execution_time],[qs]. [min_worker_time],[qs].[max_worker_time],[qs].[min_logical_reads],[qs].[max_logical_reads],[qs].[min_elapsed_time],[qs].[max_elapsed_time],[qp].[query_plan ]OD [sys].[dm_exec_query_stats] [qs]CROSS APPLY [sys].[dm_exec_sql_text]([qs].plan_handle) [st]CROSS APPLY [sys].[dm_exec_query_plan]([qs].plan_handle) [qp] WHERE [st].[text] LIKE '%usp_GetCustomerStats%'AND OBJECT_NAME([st].[objectid]) NENÍ NULL;
Cache plánu:Na začátku
Plán dotazů pro uloženou proceduru pomocí SQL Sentry Plan ExplorerViděl jsem, že plán byl vytvořen 2014-04-08 18:59:39.850. S plánem v mezipaměti jsem zastavil relaci Extended Events:
ZMĚNIT RELACI UDÁLOSTI [StatsUpdate_QueryPerf]VE STAVU SERVERU =STOP;Dále jsem do tabulky pomocí tohoto skriptu přidal asi 47 milionů řádků dat, což je hodně nad prahovou hodnotou nutnou pro zneplatnění aktuální statistiky. Po přidání údajů jsem ověřil počet řádků v tabulce:
Big_SalesOrderHeader CI:Po načtení datNež jsem znovu spustil svou uloženou proceduru, zkontroloval jsem mezipaměť plánu, abych se ujistil, že se nic nezměnilo, a ověřil jsem, že se statistiky ještě neaktualizovaly. Pamatujte, že i když byly statistiky v tomto okamžiku zrušeny, nebudou aktualizovány, dokud nebude proveden dotaz, který statistiku používá (pro informaci:Vysvětlení, kdy se statistika automaticky aktualizuje). V posledním kroku jsem znovu spustil relaci Extended Events a poté jsem několikrát spustil uloženou proceduru. Po těchto provedeních jsem znovu zkontroloval mezipaměť plánu:
Cache plánu:po načtení datPočet provedení je opět 8, a když se podíváme na create_time plánu, můžeme vidět, že se změnil na 2014-04-08 19:32:52.913. Pokud plán zkontrolujeme, uvidíme, že je stejný, i když byl plán překompilován:
Plán dotazů pro uloženou proceduru pomocí SQL Sentry Plan ExplorerAnalýza výstupu rozšířených událostí
Vzal jsem první soubor Extended Events – před načtením dat – a otevřel jsem ho v SSMS, pak jsem použil filtr, aby byly uvedeny pouze příkazy z uložené procedury:
Výstup rozšířených událostí:Po úvodním spuštění SPMůžete vidět, že existuje osm (8) spuštění uložené procedury, přičemž trvání dotazu se mírně liší.
Vzal jsem druhý soubor Extended Events – po načtení dat – otevřel jej SSMS a znovu filtroval, aby byly uvedeny pouze příkazy z uložené procedury a také události auto_stats:
Výstup rozšířených událostí:Spuštění SP po načtení datVýstup je zkrácen, protože není vše potřebné k zobrazení hlavního výsledku. Modře zvýrazněné položky představují první provedení uložené procedury a všimněte si, že existuje několik kroků – aktualizace statistik je součástí provedení. Spustí se příkaz SELECT (attach_activity_id.seq =3) a poté se provedou aktualizace statistik. V našem příkladu máme ve skutečnosti aktualizace tří statistik. Po dokončení poslední aktualizace (attach_activity_id.seq =11) se spustí a dokončí uložená procedura (attach_activity_id.seq =13 a attachment_activity_id.seq =14). Je zajímavé, že pro uloženou proceduru existuje druhá událost sp_statement_starting (pravděpodobně se první ignoruje), takže celková doba trvání uložené procedury se vypočítá bez aktualizace statistik.
V tomto scénáři automatická aktualizace statistik okamžitě – to znamená, když se spustí dotaz, který používá neplatné statistiky – způsobí, že dotaz bude probíhat déle, přestože trvání dotazu založené na události sp_statement_completed je stále kratší než 14 000. není přínosem pro výkon dotazů, protože plán je přesně stejný před a po aktualizaci statistik. V tomto scénáři se plán dotazu a doba provádění po přidání dalších dat do tabulky nezmění, takže aktualizace statistik pouze omezuje její výkon. Nyní se podívejme, co se stane, když povolíme možnost Automatické aktualizace statistik asynchronně.
Test, verze 2
Začneme obnovením do zálohy, kterou jsem vzal před zahájením prvního testu. Znovu jsem vytvořil uloženou proceduru a poté jsem změnil možnost databáze na asynchronní aktualizaci statistik:
POUŽÍVEJTE [master];DATABÁZE BRANKŮ [AdventureWorks2012_Big] NASTAVTE AUTO_UPDATE_STATISTICS_ASYNC ON S NO_WAITGOSpustil jsem relaci Extended Events a znovu jsem několikrát provedl uloženou proceduru s použitím různých CustomerID:
ALTER EVENT SESSION [StatsUpdate_QueryPerf]NA SERVERSTATE =START;GO EXEC Sales.usp_GetCustomerStats11331, '2012-08-01 00:00:00.000', '2012-08-391 2012-08-391:59233. '2013-01-01 00:00:00.000', '2013-01-31 23:59:59.997'GOEXEC Sales.usp_GetCustomerStats11506, '2012-11-01 00:00'20.00.01 :59:59.997'GOEXEC Sales.usp_GetCustomerStats17061, '2013-01-01 00:00:00.000', '2013-01-31 23:59:59.997'GOEXEC 377ust2omer131Get:0001001 00.000', '2013-03-31 23:59:59.997'GOEXEC Sales.usp_GetCustomerStats15131, '2013-02-01 00:00:00.000', '2013-03759 Sales'2013-0375995om. '2012-10-01 00:00:00.000', '2012-10-31 23:59:59.997'GOEXEC Sales.usp_GetCustomerStats15750, '2013-03-01 00:03'31'00.02 :59:59.997'GOOvěřil jsem počet provedení a plán dotazem na mezipaměť procedur:
Cache plánu:Na začátku, Test 2
Plán dotazů pro uloženou proceduru pomocí SQL Sentry Plan ExplorerPro tento test byl plán vytvořen dne 2014-04-08 21:15:55.490. Zastavil jsem relaci Extended Events a znovu přidal asi 47 milionů řádků dat do tabulky pomocí stejného dotazu jako předtím.
Jakmile byla data přidána, zkontroloval jsem mezipaměť plánu, abych se ujistil, že se nic nezměnilo, a ověřil jsem, že statistiky se ještě neaktualizovaly. Nakonec jsem znovu spustil relaci Extended Events a pak jsem uloženou proceduru spustil ještě osmkrát. Při posledním nahlédnutí do mezipaměti plánu bylo zjištěno, že počet spuštění je 16 a čas vytvoření 2014-04-08 21:15:55.490. Exekuce_count a create_time ukazují, že se statistiky neaktualizovaly, protože plán ještě nebyl vyprázdněn z mezipaměti (pokud ano, měli bychom pozdější create_time a exekuční počet 8).
Cache plánu:Po načtení dat, test 2Pokud otevřeme výstup Extended Events po načtení dat v SSMS a znovu filtrujeme, abychom viděli pouze příkazy z uložené procedury a také události auto_stats, najdeme toto (všimněte si, že výstup je rozdělen na dva snímky obrazovky):
Výstup rozšířených událostí:Test 2, provedení SP po načtení dat, část I
Výstup rozšířených událostí:Test 2, Spuštění SP po načtení dat, část IIUdálosti pro provedení prvního volání uložené procedury jsou zvýrazněny modře – začínají na 2014-04-08 21:54:14.9480607 a je zde sedm (7) událostí. Všimněte si, že existují tři (3) události auto_stats, ale žádná z nich není ve skutečnosti dokončena, jak jsme viděli, když byla deaktivována možnost Automaticky aktualizovat statistiky asynchronně. Všimnete si, že automatická aktualizace pro jednu ze statistik začíná téměř okamžitě (2014-04-08 21:54:14.9481288) a tři události mají vedle sebe červený text „Stat Update #1“. Tato aktualizace statistik skončí v 2014-04-08 21:54:16.5392219, necelé dvě sekundy po jejím spuštění, ale po dokončení všech ostatních provedení procedury. To je důvod, proč exekuční_počet z sys.dm_exec_query_stats ukazuje 16. Z výstupu XE můžeme vidět, že ostatní aktualizace statistik se poté dokončí (Stat Update #2 a Stat Update #3). Všechny aktualizace jsou asynchronní s prováděním počáteční uložené procedury.
Shrnutí
Jak vidíte, automatické aktualizace statistik mají potenciál negativně ovlivnit výkon dotazů. Stupeň dopadu bude záviset na množství dat, které je třeba přečíst pro aktualizaci statistiky, a na systémových prostředcích. V některých případech se výkon dotazu zvyšuje pouze o milisekundy a je pro uživatele s největší pravděpodobností nepostřehnutelný. Jindy se může doba trvání dramaticky prodloužit, což pak ovlivní zkušenost koncového uživatele. V případě, že se plán dotazů po aktualizaci statistiky nezmění, je vhodné zvážit povolení možnosti Automaticky aktualizovat statistiky asynchronně, aby se zmírnil dopad na výkon dotazů.