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

SQL Server Interní:Plán Caching Pt. I – Plány opětovného použití

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.

Podívejte se na Kalenovy nedávné blogy o problematických operátorech zde.

Vytváření plánů diagnostiky SQL serveru může být nákladné, protože optimalizátor dotazů musí být schopen najít dobrý plán pro jakýkoli odeslaný právní dotaz. Optimalizátor vyhodnocuje vícenásobné pořadí spojení, více indexů. a různé typy spojovacích a seskupovacích algoritmů v závislosti na vašem dotazu a použitých tabulkách. Pokud je stejný dotaz znovu spuštěn, může SQL Server ušetřit spoustu prostředků opětovným použitím existujícího plánu. Ale ne vždy je možné znovu použít stávající plán a není to vždy dobré. V následujících dvou článcích se podíváme na to, kdy je plán znovu použit a kdy je znovu zkompilován.

Nejprve se podíváme na různé varianty plánů a zobrazení metadat, které nejčastěji používám, abych se podíval na to, co je v mé mezipaměti plánu. Napsal jsem svůj vlastní pohled, který poskytuje informace, které považuji za nejužitečnější. SQL Server ukládá do mezipaměti šest různých typů plánů dotazů, ale pouze dva se běžně používají pro ladění mezipaměti plánu. Jedná se o KOMPILOVANÝ PLÁN a KOMPILOVANÝ PLÁN STUB. Můj pohled odfiltruje všechny kromě těchto dvou typů objektů mezipaměti. KOMPILOVANÉ PLÁNY přicházejí ve třech variantách:AD HOC, PŘIPRAVENÉ a PROC. Vyjádřím se ke všem třem druhům.

I když se díváme jen na KOMPILOVANÉ PLÁNY, stále je v mezipaměti spousta plánů, které je obvykle třeba ignorovat, protože jsou generovány samotným SQL Serverem. Patří mezi ně plány hledající souborový proud nebo indexy fulltextového vyhledávání nebo interní dotazy pracující s In-memory OLTP. Můj pohled tedy přidává filtry, abych se pokusil odstavit většinu plánů, které mě nezajímají. Můžete si stáhnout skript pro vytvoření tohoto zobrazení s názvem sp_cacheobjects , zde

I přes všechny filtry, které můj pohled používá, jsou v mezipaměti stále některé vlastní interní dotazy SQL Serveru; Při testování v této oblasti obvykle často vymazávám mezipaměť plánu. Nejjednodušší způsob, jak vymazat VŠECHNY plány z mezipaměti, je příkaz:DBCC FREEPROCCACHE.

Adhoc kompilované plány

Nejjednodušším typem plánu je Adhoc. Používá se pro základní dotazy, které nespadají do jiné kategorie. Pokud jste si stáhli můj skript a vytvořili můj pohled sp_cacheobjects, můžete spustit následující. Jakákoli verze databáze AdventureWorks by měla fungovat. Tento skript vytvoří kopii tabulky a vytvoří na ní několik jedinečných indexů. Také masíruje mezisoučet, aby se odstranily všechny desetinné číslice.

USE AdventureWorks2016;
GO
DROP TABLE IF EXISTS newsales;
GO
-- Make a copy of the Sales.SalesOrderHeader table
SELECT * INTO dbo.newsales
FROM Sales.SalesOrderHeader;
GO
UPDATE dbo.newsales
SET SubTotal = cast(cast(SubTotal as int) as money);
GO
CREATE UNIQUE index newsales_ident
    ON newsales(SalesOrderID);
GO
CREATE INDEX IX_Sales_SubTotal ON newsales(SubTotal);
GO
-- Adhoc query plan reuse
DBCC FREEPROCCACHE;
GO
-- adhoc query
SELECT * FROM dbo.newsales
WHERE SubTotal = 4;
GO
SELECT * FROM sp_cacheobjects;
GO

V mém výstupu vidíte dva plány Adhoc. Jeden je pro příkaz SELECT z novinek tabulka a druhá je pro SELECT z mých sp_cacheobjects Pohled. Protože je plán uložen do mezipaměti, pokud znovu spustíte PŘESNĚ stejný dotaz, lze stejný plán znovu použít a uvidíte počet použití zvýšení hodnoty. Má to však háček. Aby bylo možné plán Adhoc znovu použít, musí být řetězec SQL naprosto přesně stejný. Pokud změníte jakékoli znaky v SQL, dotaz nebude rozpoznán jako stejný dotaz a vygeneruje se nový plán. Pokud dokonce přidám mezeru, komentář nebo nový konec řádku, není to stejný řetězec. Pokud změním velikost písmen, znamená to, že existují různé hodnoty kódu ASCII, tedy ne stejný řetězec.

Můžete si to vyzkoušet sami spuštěním různých variant mého prvního příkazu SELECT z novinek stůl. Pro každý z nich uvidíte v mezipaměti jiný řádek. Poté, co jsem provedl několik variant – změnil jsem číslo, které jsem hledal, změnil velikost písmen, přidal komentář a nový řádek, v mezipaměti vidím následující. SELECT z mého pohledu se znovu používá, ale vše ostatní má počet použití hodnotu 1.

Dalším požadavkem pro opětovné použití plánu dotazů Adhoc je, že relace, na které je dotaz spuštěn, musí mít v platnosti stejné možnosti SET . Ve výstupu je další sloupec, který můžete vidět napravo od textu dotazu, nazvaný SETOPTS. Toto je bitový řetězec s bitem pro každou relevantní možnost SET. Pokud změníte jednu z možností, například SET ANSI_NULLS OFF, bitový řetězec se změní a stejný plán s původním bitovým řetězcem nelze znovu použít.

Připravené kompilované plány

Druhým typem kompilovaného plánu uloženého v mezipaměti je PŘIPRAVENÝ plán. Pokud váš dotaz splňuje určitý soubor požadavků. Ve skutečnosti může být automaticky parametrizován. Zobrazuje se v metadatech jako PŘIPRAVENO a řetězec SQL zobrazuje značku parametru. Zde je příklad:

Plán PREPARED zobrazuje značku parametru jako @1 a nezahrnuje skutečnou hodnotu. Všimněte si, že existuje řádek pro dotaz ADHOC se skutečnou hodnotou 5555, ale to je ve skutečnosti pouze „skořápka“ skutečného dotazu. Neukládá do mezipaměti celý plán, ale pouze dotaz a několik identifikačních detailů, aby pomohl procesoru dotazu najít parametrizovaný plán v mezipaměti. Všimněte si velikosti (použité stránky ) je mnohem menší než plán PŘIPRAVENÝ.

Výchozí režim parametrizace, nazývaný JEDNODUCHÁ parametrizace, je extrémně přísný ohledně toho, jaké plány lze parametrizovat. Ve výchozím nastavení jsou parametrizovatelné opravdu jen ty nejjednodušší dotazy. Dotazy, které obsahují JOIN, GROUP BY, OR a mnoho dalších relativně běžných dotazových konstrukcí, zabraňují parametrizaci dotazu. Kromě toho, že nemáte žádnou z těchto konstrukcí, je pro JEDNODUCHOU parametrizaci nejdůležitější, že dotaz je BEZPEČNÝ. To znamená, že existuje pouze jeden možný plán bez ohledu na to, jaké hodnoty jsou předány pro jakékoli parametry. (Dotaz bez jakýchkoli parametrů může být samozřejmě také BEZPEČNÝ.) Můj dotaz hledá přesnou shodu ve sloupci SalesOrderID , který má jedinečný index. Stávající neclusterovaný index by tedy mohl být použit k nalezení libovolného shodného řádku. Bez ohledu na to, jakou hodnotu použiji, 55555 nebo něco jiného, ​​nikdy nebude více než jeden řádek, což znamená, že plán bude stále dobrý.

V mém příkladu plánu dotazů Adhoc jsem hledal odpovídající hodnoty pro Subsoučet . Nějaký Mezisoučet hodnoty se vyskytují několikrát nebo vůbec, takže neshlukovaný index by byl dobrý. Jiné hodnoty se však mohou vyskytovat mnohokrát, takže index NENÍ užitečný. Plán dotazů tedy není BEZPEČNÝ a dotaz nelze parametrizovat. Proto jsme viděli Adhoc plán pro můj první příklad.

POKUD máte dotazy s JOIN nebo jinými nepovolenými konstrukcemi, můžete SQL Serveru říci, aby byl agresivnější v parametrizaci, změnou možnosti databáze:

ALTER DATABASE AdventureWorks2016 SET parameterization FORCED;
GO

Nastavení databáze na FORCED parametrizaci znamená, že SQL Server bude parametrovat mnohem více dotazů, včetně dotazů s JOIN, GROUP BY, OR atd. Znamená to také, že SQL Server může parametrizovat dotaz, který není BEZPEČNÝ. Může přijít s plánem, který je dobrý, když je vráceno jen několik řádků, a pak plán znovu použít, když je vráceno mnoho řádků. To může skončit s velmi suboptimálním výkonem.

Poslední možností pro připravený plán je, když plán výslovně připravíte. Toto chování je obvykle vyvoláno prostřednictvím aplikace s SQLPrepare a SQLExecute API. Označením parametrů určíte, co je dotaz, určíte datové typy a určíte konkrétní hodnoty, které se mají použít. Stejný dotaz pak lze spustit znovu s různými konkrétními hodnotami a použije se stávající plán. Ačkoli použití explicitně připravených plánů může být možné v případech, kdy SQL Server neparametrizuje a vy si to přejete, nebrání to SQL Serveru v použití plánu, který NENÍ vhodný pro následné parametry. Potřebujete otestovat své dotazy s mnoha různými vstupními hodnotami a ujistit se, že dosáhnete očekávaného výkonu, pokud a když je plán znovu použit.

Metadata (např. moje sp_cacheobjects view) pouze zobrazuje PŘIPRAVENO pro všechny tři typy plánů:VYNUCENÁ a JEDNODUCHÁ autoparametrizace a EXPLICITNÍ parametrizace.

Proc Compiled Plans

Konečný objtype hodnota pro Compiled Plans je pro uloženou proceduru, která je zobrazena jako Proc. Pokud je to možné, jsou uložené procedury tou nejlepší volbou pro opakovaně použitelný kód, a to díky jejich snadné správě ze samotného serveru, ale to neznamená, že je zaručeno, že vždy podávají nejlepší výkon. Stejně jako při použití možnosti parametrizace FORCED (a také explicitní parametrizace), uložené procedury používají „sniffování parametrů“. To znamená, že první předaná hodnota parametru určuje plán. Pokud následná spuštění fungují dobře se stejným plánem, pak není sniffování parametrů problém a může být skutečně přínosné, protože nám šetří náklady na rekompilaci a reoptimalizaci. Pokud by však následná provedení s jinými hodnotami neměla používat původní plán, pak máme problém. Ukážu vám příklad sniffování parametrů, které způsobuje problém

Vytvořím uloženou proceduru na základě novinek stůl, který jsme používali dříve. Procedura bude mít jeden dotaz, který filtruje na základě SalesOrderID sloupec, na kterém jsme postavili neshlukovaný index. Dotaz bude založen na nerovnosti, takže pro některé hodnoty může dotaz vrátit jen několik řádků a použít index, a pro jiné hodnoty může dotaz vrátit HODNĚ řádků. Jinými slovy, dotaz není BEZPEČNÝ.

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

Použiji možnost SET STATISTICS IO ON, abych viděl, kolik práce se dělá, když je procedura provedena. Nejprve jej provedu s parametrem, který vrací pouze několik řádků:

SET STATISTICS IO ON
GO
EXEC get_sales_range 43700;
GO

Hodnota STATISTICS IO hlásí, že vrácení 41 řádků trvalo 43 logických čtení. To je normální u indexu bez klastrů. Nyní provedeme proceduru znovu s mnohem větší hodnotou.

EXEC get_sales_range 66666;
GO
SELECT * FROM sp_cacheobjects;
GO
This time, we see that SQL Server used a whole lot more reads:

Ve skutečnosti je to skenování tabulky na novinkách tabulka trvá pouze 843 čtení, takže je to mnohem horší výkon než skenování tabulky. sp_cacheobjects zobrazení nám ukazuje, že plán PROC byl znovu použit pro toto druhé provedení. Toto je příklad toho, kdy sniffování parametrů NENÍ dobrá věc.

Co tedy můžeme dělat, když je problém s čicháním parametrů? V příštím příspěvku vám řeknu, kdy SQL Server přijde s novým plánem a nebude znovu používat staré. Podíváme se na to, jak můžete vynutit (nebo podpořit) rekompilaci, a také uvidíme, kdy SQL Server automaticky znovu zkompiluje vaše dotazy.

Spotlight Cloud může způsobit revoluci ve vašem monitorování výkonu a diagnostice serveru SQL. Začněte s bezplatnou zkušební verzí pomocí odkazu níže:


  1. Jak používat Prisma

  2. Co je ekvivalent DATALENGTH() v MySQL?

  3. Jak provedu GROUP BY na aliasovém sloupci na serveru MS-SQL?

  4. Oracle.DataAccess.Client.OracleException ORA-03135:kontakt ztraceného připojení