it-swarm-eu.dev

Parametr Sniffing vs VARIABLES vs Recompile vs OPTIMIZE FOR UNKNOWN

Dnes ráno jsme tedy měli problémy, které způsobují problémy (30 sekund + doba běhu). Rozhodli jsme se zkontrolovat, zda je na vině parametrické čichání. Takže jsme přepsali proc a nastavili příchozí parametry na proměnné tak, abychom porazili čichání parametrů. Vyzkoušený/pravdivý přístup. Bam, doba dotazu se zlepšila (méně než 1 s). Při pohledu na plán dotazů byla vylepšení nalezena v indexu, který originál nepoužíval.

Jen abychom ověřili, že jsme nedostali falešně pozitivní, udělali jsme dbcc freeproccache na původním proc a reran, abychom zjistili, zda by zlepšené výsledky byly stejné. K našemu překvapení však původní postup stále běží pomalu. Zkusili jsme to znovu s ODPORUČUJEME, stále pomalý (zkusili jsme překompilovat volání na proc a uvnitř proc je to samo). Dokonce jsme restartovali server (samozřejmě dev box).

Takže moje otázka zní: jak může být vyčichání parametrů na vině, když dostaneme stejný pomalý dotaz na mezipaměť prázdného plánu ... neměly by existovat žádné parametry, které by snif ???

Jsme místo toho ovlivněni statistikami tabulky, které se netýkají mezipaměti plánu. A pokud ano, proč by nastavení příchozích parametrů na proměnné pomohlo ??

V dalším testování jsme také zjistili, že vložení OPTION (OPTIMALIZACE PRO NEZNÁMÉ) na interní prvky proc [~ # ~] udělalo [~ # ~] získat očekávaný vylepšený plán.

Takže, někteří z vás chytřejší než já, můžete dát nějaké vodítko, co se děje v zákulisí k dosažení tohoto typu výsledku?

Na další poznámku se pomalý plán také přeruší brzy s důvodem GoodEnoughPlanFound, zatímco rychlý plán nemá žádný skutečný důvod pro zrušení ve skutečném plánu.

Celkem

  • Vytváření proměnných z příchozích parametrů (1 sec)
  • s recompile (30+ sec)
  • dbcc freeproccache (30+ s)
  • OPTION (OPTIMALIZACE PRO UKNOWN) (1 sec)

UPDATE:

Viz plán pomalého provádění zde: https://www.dropbox.com/s/cmx2lrsea8q8mr6/plan_slow.xml

Podívejte se na plán rychlého provedení zde: https://www.dropbox.com/s/b28x6a01w7dxsed/plan_fast.xml

Poznámka: tabulka, schéma, názvy objektů se změnily z bezpečnostních důvodů.

41
RThomas

Dotaz je

SELECT SUM(Amount) AS SummaryTotal
FROM   PDetail WITH(NOLOCK)
WHERE  ClientID = @merchid
       AND PostedDate BETWEEN @datebegin AND @dateend 

Tabulka obsahuje 103 129 000 řádků.

Rychlý plán vyhledá ClientId s reziduálním predikátem k datu, ale k získání Amount musí provést 96 vyhledávání. Sekce <ParameterList> V plánu je následující.

        <ParameterList>
          <ColumnReference Column="@dateend" 
                           ParameterRuntimeValue="'2013-02-01 23:59:00.000'" />
          <ColumnReference Column="@datebegin" 
                           ParameterRuntimeValue="'2013-01-01 00:00:00.000'" />
          <ColumnReference Column="@merchid" 
                           ParameterRuntimeValue="(78155)" />
        </ParameterList>

Pomalý plán vyhledá podle data a má vyhledávání, aby vyhodnotil zbytkový predikát na ClientId a načíst částku (odhadovaný 1 vs skutečný 7 388 383). Sekce <ParameterList> Je

        <ParameterList>
          <ColumnReference Column="@EndDate" 
                           ParameterCompiledValue="'2013-02-01 23:59:00.000'" 
                           ParameterRuntimeValue="'2013-02-01 23:59:00.000'" />
          <ColumnReference Column="@BeginDate" 
                           ParameterCompiledValue="'2013-01-01 00:00:00.000'"               
                           ParameterRuntimeValue="'2013-01-01 00:00:00.000'" />
          <ColumnReference Column="@ClientID" 
                           ParameterCompiledValue="(78155)" 
                           ParameterRuntimeValue="(78155)" />
        </ParameterList>

V tomto druhém případě ParameterCompiledValue není prázdný . SQL Server úspěšně čichal hodnoty použité v dotazu.

Kniha „SQL Server 2005 Praktické odstraňování problémů“ obsahuje toto téma o používání lokálních proměnných

Použití lokálních proměnných k porážce čichání parametrů je docela běžný trik, ale rady OPTION (RECOMPILE) a OPTION (OPTIMIZE FOR) h ... jsou obecně elegantnější a mírně méně riziková řešení


Poznámka

V SQL Server 2005 umožňuje kompilace na úrovni příkazu kompilaci individuálního příkazu v uložené proceduře, aby byla odložena až těsně před prvním provedením dotazu. Do té doby bude známa hodnota místní proměnné. Teoreticky by to mohl server SQL Server využít k vyčíslení hodnot lokálních proměnných stejným způsobem, jako čichání parametrů. Však protože bylo běžné používat místní proměnné k porážce šňupání parametrů v SQL Server 7.0 a SQL Server 2000+, šňupání lokálních proměnných nebylo povoleno v SQL Server 2005. Může to být povoleno v budoucí verzi serveru SQL, i když je to dobré důvod použít jednu z dalších možností uvedených v této kapitole, pokud máte na výběr.


Z tohoto rychlého testu je výše popsané chování v letech 2008 a 2012 stále stejné a proměnné nejsou vyčištěny pro odložené kompilace, ale pouze v případě, že je použit výslovný tip OPTION RECOMPILE.

DECLARE @N INT = 0

CREATE TABLE #T ( I INT );

/*Reference to #T means this statement is subject to deferred compile*/
SELECT *
FROM   master..spt_values
WHERE  number = @N
       AND EXISTS(SELECT COUNT(*) FROM #T)

SELECT *
FROM   master..spt_values
WHERE  number = @N
OPTION (RECOMPILE)

DROP TABLE #T 

I přes odložené kompilace není proměnná čichána a odhadovaný počet řádků je nepřesný

Estimates vs Actual

Předpokládám tedy, že pomalý plán se týká parametrizované verze dotazu.

ParameterCompiledValue se rovná ParameterRuntimeValue pro všechny parametry, takže se nejedná o typické šňupání parametrů (kde byl plán kompilován pro jednu sadu hodnot a poté spuštěn pro jinou sadu hodnot).

Problém je v tom, že plán, který je kompilován pro správné hodnoty parametrů, je nevhodný.

Pravděpodobně narazíte na problém se vzestupnými daty popsanými zde zde a zde . Pro tabulku se 100 miliony řádků musíte vložit (nebo jinak upravit) 20 milionů, než SQL Server automaticky aktualizuje statistiku za vás. Zdá se, že když byly naposledy aktualizovány, nula řádků odpovídala rozsahu dat v dotazu, nyní však 7 milionů ano.

Mohli byste naplánovat častější aktualizace statistik, zvážit trasovací příznaky 2389 - 90 Nebo použít OPTIMIZE FOR UKNOWN, Takže to prostě padá zpět na odhady, než aby bylo možné použít aktuálně zavádějící statistiky na datetime sloupec.

To nemusí být nutné v další verzi serveru SQL (po roce 2012). související položka Connect obsahuje zajímavou odpověď

Zveřejněno společností Microsoft 28. 8. 2012 v 13:35
Provedli jsme vylepšení odhadu mohutnosti pro příští hlavní vydání, které to v zásadě opravuje. Jakmile vyjdou naše náhledy, zůstaňte naladěni na podrobnosti. Ericu

Na toto zlepšení v roce 2014 se Benjamin Nevarez dívá ke konci článku:

První pohled na nový odhadovač kardinality serveru SQL .

Zdá se, že nový odhadce mohutnosti klesne a v tomto případě použije spíše průměrnou hustotu než odhad 1 řádek.

Zde jsou některé další podrobnosti o odhadu kardinality v roce 2014 a vzestupném klíčovém problému:

Nové funkce v SQL Server 2014 - Část 2 - Odhad nové kardinality

43
Martin Smith

Takže moje otázka zní: Jak může být vyčichání parametrů na vině, když dostaneme stejný pomalý dotaz na mezipaměť prázdného plánu ... neměly by existovat žádné parametry, které by čichaly?

Když SQL Server sestaví dotaz obsahující hodnoty parametrů, sniffs specifické hodnoty těchto parametrů pro odhad kardinality (počet řádků). Ve vašem případě se při výběru plánu provádění použijí konkrétní hodnoty @BeginDate, @EndDate A @ClientID. Další informace o parametru sniffing zde a zde . Poskytuji tyto odkazy na pozadí, protože výše uvedená otázka mě nutí domnívat se, že koncept je v současné době nedokonale pochopen - při kompilaci plánu vždy existují hodnoty parametrů.

Mimochodem, to je všechno mimo bod, protože parametrické čichání zde není problém, jak zdůraznil Martin Smith. V době, kdy byl sestaven pomalý dotaz, statistiky naznačovaly, že neexistovaly žádné řádky pro čichané hodnoty @BeginDate A @EndDate:

Slow plan sniffed values

Čichané hodnoty jsou velmi nedávné a naznačují vzestupný klíčový problém Martin zmiňuje. Protože se odhaduje, že indexové vyhledávání v datech vrátí pouze jeden řádek, optimalizátor vybere plán, který posune predikát na ClientID jako zbytkový operátor Key Lookup.

Odhad z jednoho řádku je také důvodem, proč optimalizátor přestane hledat lepší plány a vrací zprávu s dobrým dostatečným plánem. Odhadované celkové náklady na pomalý plán s odhadem jednoho řádku jsou pouze 0,011313 nákladových jednotek, takže nemá smysl snažit se najít něco lepšího. Kromě, samozřejmě, hledání ve skutečnosti vrací 7 388 383 řádků, nikoli jeden, což způsobuje stejný počet vyhledávání klíčů.

Statistiky mohou být složité, aby zůstaly aktuální a užitečné na velkých tabulkách a rozdělení stránek v tomto ohledu představuje výzvy jeho vlastní. S trasovacími vlajkami 2389 a 2390 jsem neměl zvláštní úspěch, ale můžete je vyzkoušet. Novější verze serveru SQL Server (R2 SP1 a novější) mají aktualizace dynamických statistik , ale toto aktualizace statistik podle oddílů stále nejsou implementovány. Mezitím můžete naplánovat ruční aktualizaci statistik, kdykoli v této tabulce provedete významné změny.

U tohoto konkrétního dotazu bych uvažoval o implementaci indexu navrhovaného optimalizátorem během kompilace plánu rychlých dotazů:

/*
The Query Processor estimates that implementing the following index could improve
the query cost by 98.8091%.

WARNING: This is only an estimate, and the Query Processor is making this 
recommendation based solely upon analysis of this specific query.
It has not considered the resulting index size, or its workload-wide impact,
including its impact on INSERT, UPDATE, DELETE performance.
These factors should be taken into account before creating this index.
*/
CREATE NONCLUSTERED INDEX [<Name of Missing Index>]
ON [dbo].[PDetail] ([ClientID],[PostedDate])
INCLUDE ([Amount]);

Index by měl být zarovnán podle oddílů s klauzulí ON PartitionSchemeName (PostedDate), ale jde o to, že poskytnutí zjevně nejlepší cesty přístupu k datům pomůže optimalizátoru vyhnout se špatným volbám plánu, aniž by se uchýlil k OPTIMIZE FOR UNKNOWN Radám nebo staromódní zástupná řešení, jako je použití místních proměnných.

Se zlepšeným indexem bude eliminováno vyhledávání klíčů pro načtení sloupce Amount, procesor dotazu může stále provádět dynamické odstraňování oddílů a pomocí hledání vyhledejte konkrétní ClientID a časové období.

29
Paul White 9

Měl jsem stejný problém, kdy se uložená procedura zpomalila a OPTIMIZE FOR UNKNOWN a RECOMPILE rady dotazů vyřešily pomalost a urychlily dobu provádění. Následující dvě metody však neovlivnily pomalost uložené procedury: (i) Vymazání mezipaměti (ii) pomocí WITH RECOMPILE. Takže, jak jste řekl, nebylo to opravdu šňupání parametrů.

Stopové vlajky 2389 a 2390 také nepomohly. Jen aktualizuji statistiky (EXEC sp_updatestats) udělal to pro mě.

0
aali