sql >> Databáze >  >> RDS >> Database

Jednoduchá parametrizace a triviální plány — 1. část

Toto je první díl série o jednoduché parametrizaci a triviální plány . Tyto dvě funkce kompilace jsou úzce propojeny a mají podobné cíle. Oba cílí na výkon a efektivitu pro pracovní zátěže, které často předkládají jednoduchá prohlášení.

Navzdory „jednoduchým“ a „triviálním“ názvům mají oba jemné chování a detaily implementace, které mohou ztěžovat pochopení toho, jak fungují. Tato série se nezdržuje příliš dlouho u základů, ale soustředí se na méně známé aspekty, které pravděpodobně podrazí nohy i těm nejzkušenějším databázovým profesionálům.

V této první části se po rychlém úvodu podívám na efekty jednoduché parametrizace na mezipaměti plánu.

Jednoduchá parametrizace

Téměř vždy je lepší explicitně parametrizovat namísto spoléhání se na to, že to udělá server. Explicitní přístup vám dává úplnou kontrolu nad všemi aspekty procesu parametrizace, včetně toho, kde se parametry používají, přesných použitých datových typů a při opětovném použití plánů.

Většina klientů a ovladačů poskytuje specifické způsoby použití explicitní parametrizace. Existují také možnosti jako sp_executesql , uložené procedury a funkce.

Nebudu se pouštět do souvisejících problémů sniffování parametrů nebo SQL injection, protože i když jsou důležité, nejsou středem zájmu této série. Přesto byste měli psát kód tak, aby byl obojí blízko vaší mysli.

U starších aplikací nebo jiných kódů třetích stran, které nelze snadno změnit, nemusí být explicitní parametrizace vždy možná. Možná budete schopni překonat některé překážky pomocí šablon plánů. V každém případě by se jednalo o neobvyklou zátěž, která neobsahuje alespoň některé parametrizované příkazy na straně serveru.

Plány Shell

Když SQL Server 2005 zavedl Vynucená parametrizace , stávající automatická parametrizace funkce byla přejmenována na Jednoduchá parametrizace . I přes změnu terminologie jednoduchá parametrizace funguje stejně jako automatická parametrizace vždy:SQL Server se pokouší nahradit konstantní hodnoty literálu v příkazech ad hoc značkami parametrů. Cílem je omezit kompilace zvýšením opětovného použití plánu uloženého v mezipaměti.

Podívejme se na příklad pomocí databáze Stack Overflow 2010 na SQL Server 2019 CU 14. Kompatibilita databáze je nastavena na 150 a prahová hodnota nákladů pro paralelismus je nastavena na 50, aby se paralelismus prozatím vyhnul:

EXECUTE sys.sp_configure
    @configname = 'show advanced options',
    @configvalue = 1;
RECONFIGURE;
GO
EXECUTE sys.sp_configure
    @configname = 'cost threshold for parallelism',
    @configvalue = 50;
RECONFIGURE;

Příklad kódu:

-- Clear the cache of plans for this database
ALTER DATABASE SCOPED CONFIGURATION 
    CLEAR PROCEDURE_CACHE;
GO
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 2521;
GO
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 2827;
GO
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 3144;
GO
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 3151;
GO

Tyto příkazy obsahují predikáty, které se liší pouze svými konstantními doslovnými hodnotami. SQL Server úspěšně aplikuje jednoduchou parametrizaci výsledkem je parametrizovaný plán. Jediný parametrizovaný plán je použit čtyřikrát, jak můžeme vidět dotazem na mezipaměť plánu:

SELECT
    CP.usecounts,
    CP.cacheobjtype,
    CP.objtype,
    CP.size_in_bytes,
    ST.[text],
    QP.query_plan
FROM sys.dm_exec_cached_plans AS CP
OUTER APPLY sys.dm_exec_sql_text (CP.plan_handle) AS ST
OUTER APPLY sys.dm_exec_query_plan (CP.plan_handle) AS QP
WHERE 
    ST.[text] NOT LIKE '%dm_exec_cached_plans%'
    AND ST.[text] LIKE '%DisplayName%Users%'
ORDER BY 
    CP.usecounts ASC;

Výsledky ukazují Adhoc záznam mezipaměti plánu pro každý původní výpis a jeden Připraveno plán:

Čtyři plány adhoc a jeden připravený plán

A Připraveno příkaz je podobný uložené proceduře, s parametry odvozenými z doslovných hodnot nalezených v Adhoc prohlášení. Zmiňuji to jako užitečný mentální model, když přemýšlím o procesu parametrizace na straně serveru.

Všimněte si, že SQL Server ukládá do mezipaměti obojí původní text a parametrizovaný tvar. Když je jednoduchá parametrizace úspěšná, plán spojený s původním textem je Adhoc a neobsahuje úplný plán provádění. Místo toho je plán uložený v mezipaměti shell s velmi málo kromě ukazatele na Připraveno parametrizovaný plán.

XML reprezentace plánů shellu obsahovat text jako:

<ShowPlanXML xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan" Version="1.539" Build="15.0.4188.2">
<BatchSequence>
<Batch>
<Statements>
<StmtSimple 
  StatementText="SELECT U.DisplayName&#xD;&#xA;FROM dbo.Users AS U &#xD;&#xA;WHERE U.Reputation = 3151"
  StatementId="1" 
  StatementCompId="1" 
  StatementType="SELECT" 
  RetrievedFromCache="true" 
  ParameterizedPlanHandle="0x0600050090C8321CE04B4B079E01000001000000000000000000000000000000000000000000000000000000" 
  ParameterizedText="(@1 smallint)SELECT [U].[DisplayName] FROM [dbo].[Users] [U] WHERE [U].[Reputation]=@1" />
</Statements>
</Batch>
</BatchSequence>
</ShowPlanXML>

To je celý plán. ParameterizedPlanHandle body z Adhoc shell na plně parametrizovaný plán. Hodnota handle je stejná pro všechny čtyři plány shellu.

Plánovat útržky

Plány prostředí jsou menší než celý zkompilovaný plán – v příkladu 16 kB namísto 40 kB. Pokud máte mnoho příkazů používajících jednoduchou parametrizaci nebo mnoho různých hodnot parametrů, může to stále přidat značné množství paměti. Většina instancí SQL Serveru není tak zaplavená pamětí, aby si ji mohly dovolit takto plýtvat. SQL Server považuje plány shellu za velmi jednorázové, ale jejich nalezení a odstranění spotřebovává zdroje a může se stát předmětem sporu.

Můžeme snížit celkovou spotřebu paměti pro plány prostředí tím, že povolíme možnost optimalizace pro pracovní zátěže ad hoc.

EXECUTE sys.sp_configure
    @configname = 'show advanced options',
    @configvalue = 1;
RECONFIGURE;
GO
EXECUTE sys.sp_configure
    @configname = 'optimize for ad hoc workloads',
    @configvalue = 1;
RECONFIGURE;

Toto ukládá do mezipaměti malý útržek při prvním setkání s příkazem ad hoc namísto shellu. Útržek slouží jako záložka, takže si server může zapamatovat, že už viděl přesný text příkazu. Když se podruhé setkáte se stejným textem, kompilace a ukládání do mezipaměti postupují jako při optimalizaci pro zátěže ad hoc nebyly povoleny.

Znovu spusťte příklad s optimalizací pro ad hoc zátěž povoleno ukazuje vliv na mezipaměť plánu.

Zkompilované útržky plánu

Pro ad-hoc příkazy není uložen žádný plán, pouze útržek. Neexistuje žádný ParameterizedPlanHandle ukazatel na Připraveno plán, ačkoli kompletní parametrizovaný plán je mezipaměti.

Spuštění testovacích dávek podruhé (bez vymazání mezipaměti plánu) poskytne stejný výsledek jako při optimalizaci pro zátěže ad hoc nebylo povoleno – čtyři Adhoc plány shellu ukazující na Připraveno plán.

Než budete pokračovat, resetujte optimalizaci pro zátěže ad hoc nastavení na nulu:

EXECUTE sys.sp_configure
    @configname = 'optimize for ad hoc workloads',
    @configvalue = 0;
RECONFIGURE;

Plánovat limity velikosti mezipaměti

Ať už jsou použity planžety nebo planžety, všechny tyto Adhoc mají stále své nevýhody záznamy v mezipaměti. Už jsem zmínil celkové využití paměti, ale každá mezipaměť plánu má také maximální počet záznamů. I tam, kde celkové využití paměti není důležité, může existovat samotné množství.

Limity lze zvýšit pomocí dokumentovaného příznaku trasování 174 (počet položek) a příznaku trasování 8032 (celková velikost). V závislosti na pracovní zátěži a dalších požadavcích na paměť to nemusí být nejlepší řešení. Koneckonců to znamená pouze ukládání do mezipaměti Adhoc s nízkou hodnotou plány, odnímání paměti ostatním potřebám.

Ukládání do mezipaměti pouze připravených plánů

Pokud pracovní zátěž zřídka vydává ad-hoc dávky s přesně stejný text příkazu, skořápky plánu ukládání do mezipaměti nebo útržky plánu jsou plýtváním zdrojů. Spotřebovává to paměť a může způsobit spory při plánech SQL úložiště mezipaměti (CACHESTORE_SQLCP ) je třeba zmenšit, aby se vešel do nakonfigurovaných limitů.

Ideální by bylo parametrizovat příchozí ad-hoc dávky, ale pouze cache parametrizovanou verzi. To stojí za to, protože budoucí příkazy ad-hoc je třeba parametrizovat, než je lze přiřadit k parametrizovanému plánu uloženému v mezipaměti. Na druhou stranu by k tomu stejně došlo, protože jsme již uvedli přesné textové shody jsou pro cílovou zátěž vzácné.

Pro úlohy, které těží z jednoduché parametrizace, ale nikoli z ukládání do mezipaměti Adhoc položek, existuje několik možností.

Nezdokumentovaný příznak trasování

První možností je povolit nedokumentovaný příznak trasování 253. To zabrání ukládání do mezipaměti Adhoc úplně plánuje. Neomezuje pouze počet takových plánů nebo jim nebrání „zůstat“ v mezipaměti, jak se někdy navrhovalo.

Příznak trasování 253 lze povolit na úrovni relace – s omezením jeho účinků pouze na toto připojení – nebo v širším měřítku jako globální nebo spouštěcí příznak. Funguje také jako nápověda k dotazu, ale jejich použití brání jednoduché parametrizaci, která by zde byla kontraproduktivní. Částečný seznam věcí, které brání jednoduché parametrizaci, je uveden v technickém dokumentu společnosti Microsoft, Plán ukládání do mezipaměti a rekompilace v SQL Server 2012.

S aktivním příznakem trasování 253 před kompilací dávky , pouze Připraveno příkazy jsou uloženy v mezipaměti:

ALTER DATABASE SCOPED CONFIGURATION 
    CLEAR PROCEDURE_CACHE;
GO
-- Do not cache ad-hoc plans
DBCC TRACEON (253);
GO
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 2521;
GO
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 2827;
GO
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 3144;
GO
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 3151;
GO
-- Cache ad-hoc plans again
DBCC TRACEOFF (253);
GO

Dotaz mezipaměti plánu potvrzuje pouze Připraveno příkaz je uložen do mezipaměti a znovu použit.

Ukládá se pouze připravený příkaz

Nevyrovnávací dávka

Druhou možností je zahrnout příkaz, který označí celou dávku jako uncacheable . Vhodné příkazy se často týkají bezpečnosti nebo jsou nějakým způsobem jinak citlivé.

Může to znít neprakticky, ale existuje několik zmírnění. Za prvé, citlivý příkaz nemusí být proveden – stačí jej přítomný . Když je tato podmínka splněna, uživatel spouštějící dávku ani nepotřebuje oprávnění provést citlivé prohlášení. Pozorně si všimněte, že účinek je omezen na dávku obsahující citlivé prohlášení.

Níže jsou uvedeny dva vhodně citlivé příkazy a příklady použití (s testovacími příkazy nyní v jedné dávce):

ALTER DATABASE SCOPED CONFIGURATION 
    CLEAR PROCEDURE_CACHE;
GO
-- Prevent caching of all statements in this batch.
-- Neither KEY nor CERTIFICATE need to exist.
-- No special permissions are needed.
-- GOTO is used to ensure the statements are not executed.
GOTO Start
    OPEN SYMMETRIC KEY Banana 
        DECRYPTION BY CERTIFICATE Banana;
Start:
 
/* Another way to achieve the same effect without GOTO
IF 1 = 0
BEGIN
    CREATE APPLICATION ROLE Banana 
    WITH PASSWORD = '';
END;
*/
 
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 2521;
 
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 2827;
 
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 3144;
 
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 3151;
GO

Připraveno plány vytvořené jednoduchou parametrizací jsou stále ukládány do mezipaměti a znovu používány, přestože byla nadřazená dávka označena jako neuložitelná do mezipaměti.

Ukládá se pouze připravený příkaz

Ani jedno řešení není ideální, ale dokud Microsoft neposkytne zdokumentované a podporované řešení tohoto problému, jsou to nejlepší možnosti, o kterých vím.

Konec 1. části

Na toto téma je toho mnohem více. Druhá část se bude zabývat datovými typy přiřazenými při jednoduché parametrizaci je zaměstnán.


  1. Jaký je nejlepší způsob připojení mezi androidem a databází Oracle?

  2. Mysql nebo/a přednost?

  3. Jak vypočítat procento růstu týden po týdnu v MySQL

  4. percentily z dat histogramu