sql >> Databáze >  >> RDS >> Sqlserver

SQL Server Interní:Plán Caching Pt. II – Plány překompilování

Toto je součástí řady SQL Server Internals Plan Caching. Určitě si přečtěte Kalenův první příspěvek na toto téma.

SQL Server existuje více než 30 let a já pracuji se serverem SQL Server téměř stejně dlouho. V průběhu let (a desetiletí!) a verzí tohoto neuvěřitelného produktu jsem viděl mnoho změn. V těchto příspěvcích se s vámi podělím o to, jak se dívám na některé funkce nebo aspekty SQL Serveru, někdy spolu s trochou historické perspektivy

V mém předchozím článku , mluvil jsem o diagnostice serveru SQL, včetně různých možností, které má SQL Server pro opětovné použití plánu dotazů. Podívali jsme se na tři typy plánů dotazů:adhoc, připravené a procedurální. Diskusi jsem zakončil pohledem na nevhodné opětovné použití plánu, ke kterému může dojít, když SQL Server použije sniffování parametrů v nesprávných situacích. Pokud je plán založen na počáteční hodnotě, která způsobí, že optimalizátor vygeneruje plán vhodný pro tuto hodnotu, a poté je stejný plán použit pro jinou hodnotu, plán již nemusí být optimální.

Co tedy můžeme dělat, když je problém s čicháním parametrů? Můžeme donutit SQL Server, aby přišel s novým plánem. Obvykle nazýváme akt vymýšlení nového plánu ‚rekompilace‘, ale pravděpodobně by se to mělo nazývat ‚reoptimalizace‘. Většina lidí však používá termín „překompilovat“, takže to zde použiji.

Pokud je problémem nevhodné použití sniffování parametrů, jednoduchým řešením je prostě říct SQL Serveru, aby přišel s novým plánem. Pro jednotlivé příkazy, jako jsou plány PREPARED, které byly autoparametrizovány, můžeme do dotazu přidat nápovědu RECOMPILE. Pomocí parametrizovaného FORCED (diskutovaného v předchozím článku) bude tento dotaz parametrizován.

SELECT * FROM dbo.newsales 
WHERE SalesOrderID < @num;

Pokud se chceme ujistit, že při každém spuštění tohoto dotazu získáme nový plán s potenciálně výrazně odlišnými hodnotami pro @num, můžeme přidat nápovědu RECOMPILE, jak je znázorněno:

SELECT * FROM dbo.newsales
  WHERE SalesOrderID < @num
OPTION (RECOMPILE);

Pro uložené procedury máme tři možnosti. Nejprve se můžeme ujistit, zda rekompilace skutečně pomůže výkonu provedením procedury s volbou PŘEKOMPILOVAT:

EXEC get_sales_range 66666 WITH RECOMPILE;

Tato možnost způsobí, že se vygeneruje nový plán pouze pro toto jedno provedení. Nebude uložen a určitě nebude znovu použit. Hodnota usecount zobrazená v sp_cacheobjects (popsaná v předchozím příspěvku) pro proceduru se nezvýší, protože původní plán není znovu použit.

Za druhé, pokud zjistíme, že provedení WITH RECOMPILE pomáhá, mohli bychom zvážit opětovné vytvoření procedury s možností RECOMPILE, v takovém případě plán nikdy znovu nepoužije a procedura se vůbec nezobrazí v mezipaměti plánu.

DROP PROC IF EXISTS get_sales_range;GO
CREATE PROC get_sales_range
   @num int
WITH RECOMPILE
AS
    SELECT * FROM dbo.newsales
    WHERE SalesOrderID < @num;
GO

Pro můj jednoduchý malý postup by použití možnosti WITH RECOMPILE pro celý postup mohlo mít smysl. Pokud je však postup složitější, nemusí mít smysl překompilovat celý postup, protože jeden příkaz způsobuje problémy. Takže třetí možností je použít nápovědu RECOMPILE pro příkaz v rámci procedury, takže to vypadá takto:

DROP PROC IF EXISTS get_sales_range;
GO
CREATE PROC get_sales_range
   @num int
AS
    SELECT * FROM dbo.newsales
    WHERE SalesOrderID < @num
    OPTION (RECOMPILE);
GO

Použití jedné z těchto možností PŘEKOMPILOVÁNÍ může přinutit SQL Server, aby na vaši žádost přišel s novým plánem. Nyní se podíváme na to, kdy vaše diagnostika SQL Serveru nabídne nový plán, když o něj nepožádáte, tj. kdy dojde k automatické rekompilaci stávajícího plánu?


K automatickému překompilování plánu dochází ve dvou typech situací:

  • Zaprvé, pokud optimalizátor zjistí, že stávající plán již není správný, obvykle kvůli změně v definicích objektů, bude muset přijít s plánem novým. Pokud například máte plán pro dotaz, který vybírá z tabulky A, a poté vypustíte několik sloupců nebo změníte datové typy sloupců v tabulce A, SQL Server znovu zkompiluje dotaz, aby vytvořil plán, který odráží změny DDL.
  • Druhou situací, kdy dojde k automatické rekompilaci, je situace, kdy SQL Server určí, že plán již nemusí být optimální kvůli změně statistiky. Ve většině případů, pokud byly statistiky některého ze sloupců nebo indexů aktualizovány od posledního sestavení plánu, bude znovu zkompilován. To ale vede k další otázce. Kdy jsou statistiky aktualizovány? Statistiky lze automaticky aktualizovat, když se v příslušných sloupcích změní dostatek řádků. Kolik je dost? Krátce o tom mluvíme.

Ve výchozím nastavení SQL Server aktualizuje statistiky automaticky kvůli možnosti databáze, která je ve výchozím nastavení ZAPNUTÁ. Ale pokud jste vlastníkem databáze (nebo SQL ‚sa‘, který se objevuje jako vlastník v každé databázi), můžete možnosti změnit. Jedna z možností se nazývá AUTO_UPDATE_STATISTICS a další se nazývá AUTO_UPDATE_STATISTICS_ASYNC. Možnost AUTO_UPDATE_STATISTICS je v databázi tempdb zapnutá, takže každá nová databáze zdědí tuto možnost. Když je tato možnost zapnutá a stroj pro provádění dotazu během zpracovávání dotazu zjistí změny dostatečného počtu řádků, provádění se pozastaví, dokud se statistiky aktualizují a poté se dotaz znovu zkompiluje. Druhá možnost, AUTO_UPDATE_STATISTICS_ASYNC, může mít potenciálně menší vliv na dobu provádění dotazu, protože provádění se nepozastavuje, a to za cenu použití možného neoptimálního plánu. U druhé možnosti, pokud prováděcí stroj zjistí potřebu aktualizovat statistiky, spustí se vlákno na pozadí, aby provedlo aktualizaci, a hlavní vlákno pokračuje ve vykonávání dotazu s původní statistikou a původním plánem. Další dotaz, který přistupuje k ovlivněným tabulkám a vidí aktualizované statistiky, dotaz znovu zkompiluje, ale nebude pozastaven a neprovede aktualizaci statistik uprostřed provádění.

Existuje několik dalších situací a také některé tipy k dotazům, které řídí, zda jsou plány překompilovány nebo ne, takže vám ukážu vývojový diagram. Podělím se s vámi o vývojový diagram, který jsem vytvořil pro své školicí kurzy na interních serverech SQL Server.

Šipka je místo, kde SQL Server začne zpracovávat vaši dávku. Nejprve zkontroluje, zda již existuje plán pro vaši dávku v mezipaměti, a pokud je odpověď NE, následuje šipku vpravo a sestaví plán. Plán se uloží do mezipaměti a SQL Server se spustí znovu. Ano, plán by měl být tentokrát v mezipaměti, takže následuje šipku dolů a zeptá se, zda byla použita nápověda nazvaná KEEP PLAN. Pokud ANO, SQL Server začne plán okamžitě provádět a neprovádí žádné další kontroly.

Další otázkou je, zda byly provedeny nějaké změny DDL. Pokud ne, ptá se na několik dalších situací, o kterých v tomto článku nebudu moci mluvit. Ve skutečnosti zde opravdu nebudu procházet každou možností. to nechám na vás. Ale pokud máte nějaké otázky nebo nejasnosti, zeptejte se jich zde v sekci komentářů nebo mi tweetujte na @sqlqueen. Upozorním na otázku zcela vpravo:Je AUTO_STATS_ASYNC ON? Zde můžete vidět, že pokud je odpověď ANO, existují dvě akce. Jedna větev pouze zahájí provádění se stávajícím plánem a druhá je vlákno na pozadí, které aktualizuje statistiky, ale pak nedělá nic jiného. Další dotaz narazí na rozhodovací pole uprostřed „Jsou k dispozici nové statistiky“ a měl by odpovědět ANO, takže dotaz bude překompilován.

Jediná další věc, o které budu mluvit, je otázka „Jsou nějaké statistiky zastaralé?“ To v podstatě znamená, že statistiky jsou zastaralé, protože bylo provedeno příliš mnoho změn. Takže teď můžeme mluvit o tom, kolik je příliš mnoho.

Ačkoli se pro velmi malé tabulky používají různé hodnoty, pro každou tabulku s více než 500 řádky by před SQL Server 2016 byly statistiky považovány za „neaktuální“, pokud počet změn ve sloupci, na kterém byly statistiky založeny, přesáhl 20. % z počtu řádků v tabulce. Takže pro tabulku s 1000 řádky to může znamenat 200 vložení, 200 aktualizací nebo 200 odstranění. Může to být 200 změn řádků nebo 5 řádků aktualizováno 40krát. SQL Server nám dokonce poskytuje funkci, která hlásí, kolik změn bylo provedeno. Budete muset vyhledat číslo stats_id pro statistiky, které vás zajímají, což by bylo index_id, pokud statistiky patří do indexu. Stats_id lze nalézt v zobrazení s názvem sys.stats. V mé tabulce novinek používám tento dotaz ke zjištění, že stats_id pro index ve sloupci Mezisoučet je 3.

SELECT name, stats_id FROM sys.stats
WHERE object_id = object_id('newsales');

Tuto hodnotu pak mohu použít k zobrazení počtu změn. Dovolte mi nejprve aktualizovat některé řádky:

UPDATE newsales
SET SubTotal = SubTotal * 0.9
WHERE SalesOrderID < 45200
(dotčeno 1541 řádků)

SELECT * FROM sys.dm_db_stats_properties(object_id('newsales'), 3);  

Ve skutečnosti je 20 % VELKÉ číslo. A u mnoha tabulek mohou dotazy těžit z aktualizovaných statistik s mnohem méně než 20 % aktualizovaných řádků. Počínaje verzí 2008R2 SP1 obsahoval SQL Server Traceflag, který můžete použít ke změně počtu řádků na posuvné měřítko, jak ukazuje následující graf:

Počínaje SQL Serverem 2016 se tento nový algoritmus s posuvným měřítkem používá ve výchozím nastavení, pokud máte úroveň kompatibility 130 nebo vyšší.

Většina automatických rekompilací plánů dotazů je způsobena změnami ve statistikách. Ale jak jsem uvedl výše, to není jediný důvod pro rekompilaci. Ale protože je to nejběžnější, může být velmi užitečné vědět, kdy a jak se statistiky aktualizují, a zajistit, aby se statistiky na vašich kritických tabulkách aktualizovaly dostatečně často, abyste měli jistotu, že získáte ty nejlepší plány!

Automaticky analyzujte data o výkonu a provádějte diagnostiku serveru SQL, abyste rychle vyřešili problémy a identifikovali servery, kde dochází ke snížení výkonu. Začněte používat Spotlight Cloud ještě dnes:


  1. Jaký je nejlepší způsob, jak vybrat minimální hodnotu z několika sloupců?

  2. Jak funguje funkce SPACE() na serveru SQL Server (T-SQL)

  3. OPENJSON „Nesprávná syntaxe poblíž klíčového slova ‚s‘.“ v SQL Server (VYŘEŠENO)

  4. Určení OID tabulky v Postgres 9.1?