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

Pokračování na Summer Performance Palooza 2013

Dne 27. června uspořádala virtuální pobočka PASS Performance své letní představení Palooza 2013 – jakési zmenšené 24 hodin PASS, ale zaměřené výhradně na témata související s výkonem. Měl jsem relaci s názvem „10 špatných návyků, které mohou zabít výkon“ a zabýval jsem se těmito 10 koncepty:

  1. VYBRAT *
  2. Slepé indexy
  3. Žádná předpona schématu
  4. Výchozí možnosti kurzoru
  5. předpona sp_
  6. Povolení přeplnění mezipaměti
  7. Široké datové typy
  8. Výchozí nastavení serveru SQL
  9. Nadměrné používání funkcí
  10. „Funguje na mém počítači“

Možná si některá z těchto témat pamatujete z prezentací, jako je moje přednáška „Špatné návyky a osvědčené postupy“ nebo naše týdenní webináře Query Tuning, které pořádám s Kevinem Klinem od začátku června do tohoto týdne. (Mimochodem, těchto 6 videí bude na YouTube k dispozici začátkem srpna.)

Moje sezení mělo 351 účastníků a dostal jsem skvělou zpětnou vazbu. Něco z toho jsem chtěl řešit.

Za prvé, problém s konfigurací:používal jsem zcela nový mikrofon a netušil jsem, že každé stisknutí klávesy bude znít jako hrom. Tento problém jsem vyřešil lepším umístěním svých periferií, ale chci se omluvit všem, kterých se to týká.

Dále stahování; paluba a vzorky jsou umístěny na místě akce. Jsou dole na stránce, ale můžete si je také stáhnout přímo zde.

Nakonec, co následuje, je seznam otázek, které byly položeny během relace, a chtěl jsem se ujistit, že jsem odpověděl na všechny, které nebyly zodpovězeny během živých Q &A. Omlouvám se, že jsem to vtěsnal za necelý měsíc , ale byly hodně otázek a nechtěl jsem je zveřejňovat po částech.

Otázka:Pokud máte proces, který může mít pro dané parametry velmi rozdílné vstupní hodnoty a výsledkem je, že plán uložený v mezipaměti není pro většinu případů optimální, je nejlepší vytvořit proces S REKOMPILOVANÍ a vzít malý Dosažení výkonu při každém spuštění?

A: Budete k tomu muset přistupovat případ od případu, protože to bude skutečně záviset na řadě faktorů (včetně složitosti plánu). Všimněte si také, že můžete provést rekompilaci na úrovni příkazů tak, že pouze dotčené příkazy musí přijmout přístup, na rozdíl od celého modulu. Paul White mi připomněl, že lidé často „opravují“ sniffování parametrů pomocí RECOMPILE , ale příliš často to znamená 2000-styl WITH RECOMPILE spíše než mnohem lepší OPTION (RECOMPILE) , který se nejen omezuje na příkaz, ale také umožňuje vkládání parametrů, které WITH RECOMPILE ne. Pokud se tedy chystáte použít RECOMPILE Chcete-li zmařit sniffování parametrů, přidejte jej do příkazu, nikoli do modulu.

Otázka:Pokud použijete možnost recompile na dynamickém SQL, zaznamenali byste velký zásah do výkonu

A: Jak je uvedeno výše, bude to záviset na nákladech a složitosti plánů a neexistuje způsob, jak říci:"Ano, vždy bude velký výkon." Také to musíte porovnat s alternativou.

O:Pokud je na insertdate seskupený index, později, když načítáme data, použijeme funkci převodu, pokud použijeme přímé srovnání, datum dotazu není čitelné, v reálném světě, co je lepší volba

A: Nejsem si jistý, co znamená „čitelný v reálném světě“. Pokud chcete výstup v určitém formátu, obvykle je lepší převod na řetězec na straně klienta. C# a většina dalších jazyků, které pravděpodobně používáte v prezentační vrstvě, jsou více než schopné formátovat výstup data/času z databáze v jakémkoli regionálním formátu, který chcete.

Otázka:Jak se určí, kolikrát je plán uložený v mezipaměti použit – existuje na internetu sloupec s touto hodnotou nebo nějaké dotazy na internetu, které tuto hodnotu udělí? A konečně, byly by takové počty relevantní pouze od posledního restartu?

A: Většina DMV je platná pouze od posledního spuštění služby a dokonce i další lze proplachovat častěji (dokonce i na vyžádání – nechtěně i záměrně). Mezipaměť plánu se samozřejmě neustále mění a plány AFAIK, které z mezipaměti vypadnou, si nezachovají svůj předchozí počet, pokud se objeví zpět. Takže i když v mezipaměti vidíte plán, nejsem 100% s jistotou, že můžete věřit počtu použití, který najdete.

To znamená, že to, co pravděpodobně hledáte, je sys.dm_exec_cached_plans.usecounts a můžete také najít sys.dm_exec_procedure_stats.execution_count pro doplnění informací o procedurách, kde jednotlivé příkazy v rámci procedur nejsou nalezeny v mezipaměti.

O:Jaké jsou problémy při upgradu db motoru na novou verzi, ale ponechání uživatelských databází ve starších režimech kompatibility?

A: Hlavní obavy kolem toho jsou schopnost používat určitou syntaxi, jako je OUTER APPLY nebo proměnné s tabulkovou funkcí. Nejsem si vědom žádných případů, kdy by použití nižší kompatibility mělo přímý dopad na výkon, ale několik věcí, které se obvykle doporučuje, je znovu sestavit indexy a aktualizovat statistiky (a přimět vašeho dodavatele, aby co nejdříve podporoval novější úroveň kompatibility). Viděl jsem, že to řeší neočekávané snížení výkonu ve značném počtu případů, ale také jsem slyšel názory, že to není nutné a možná dokonce nemoudré.

O:Na * záleží, když děláte klauzuli exist

A: Ne, alespoň pokud jde o výkon, jedna výjimka, kde SELECT * nezáleží na tom, je-li použit uvnitř EXISTS doložka. Ale proč byste měli používat * tady? Dávám přednost použití EXISTS (SELECT 1 ... – optimalizátor s nimi bude zacházet stejně, ale svým způsobem sám zdokumentuje kód a zajistí, aby čtenáři pochopili, že poddotaz nevrací žádná data (i když jim chybí velký EXISTS mimo). Někteří lidé používají NULL , a nemám ponětí, proč jsem začal používat 1, ale nacházím NULL také trochu neintuitivní.

*Poznámka* budete muset být opatrní, pokud se pokusíte použít EXISTS (SELECT * uvnitř modulu, který je vázaný na schéma:

CREATE VIEW dbo.ThisWillNotWork
WITH SCHEMABINDING
AS
  SELECT BusinessEntityID
    FROM Person.Person AS p
	WHERE EXISTS (SELECT * FROM Sales.SalesOrderHeader AS h
	  WHERE h.SalesPersonID = p.BusinessEntityID);

Zobrazí se tato chyba:

Zpráva 1054, úroveň 15, stav 6, procedura ThisWillNotWork, řádek 6
Syntaxe '*' není povolena v objektech vázaných na schéma.

Změňte jej však na SELECT 1 funguje v pohodě. Možná je to další argument, proč se vyhnout SELECT * i v tomto scénáři.

Otázka:Existuje nějaký odkaz na zdroj pro nejlepší standardy kódování?

A: V různých jazycích jsou pravděpodobně stovky. Stejně jako konvence pojmenování jsou standardy kódování velmi subjektivní věcí. Nezáleží na tom, jaká konvence se rozhodnete nejlépe pro vás; pokud máte rádi tbl předpony, zbláznit se! Upřednostňujte Pascala před bigEndianem. Chcete před názvy sloupců přidat typ dat, například intCustomerID , nebudu ti v tom bránit. Důležitější je, abyste definovali konvenci a používali ji *konzistentně.*

To znamená, že pokud chcete mé názory, nemám o ně nouzi.

O:Je XACT_ABORT něco, co lze použít v SQL Server 2008 a novějších?

A: Nevím o žádných plánech na ukončení podpory XACT_ABORT takže by to mělo fungovat dobře. Upřímně řečeno, nevidím, že se to teď používá příliš často, když máme TRY / CATCH (a THROW od SQL Server 2012).

O:Jak se vložená tabulková funkce na kříži aplikuje v porovnání se skalární funkcí, která byla volána 1 000x?

A: Netestoval jsem to, ale v mnoha případech může mít nahrazení skalární funkce funkcí s inline tabulkovou hodnotou velký dopad na výkon. Problém, který jsem zjistil, je, že provedení tohoto přepnutí může znamenat značné množství práce na systému, který byl napsán před APPLY existovaly nebo jsou stále spravovány lidmi, kteří tento lepší přístup nepřijali.

O:Mám dotaz, který běží opravdu pomalu poprvé (~1 min) a rychle (~3 sekundy) pokaždé. Kde se mám začít zabývat tím, kde se poprvé vzal problém s výkonem?

A: Napadají mě dvě věci:(1) zpoždění souvisí s dobou kompilace nebo (2) zpoždění souvisí s množstvím dat, která se načítají pro uspokojení dotazu, a když poprvé musí přijít z disku. a ne paměť. Pro (1) můžete provést dotaz v SQL Sentry Plan Explorer a stavový řádek vám ukáže čas kompilace pro první a následující vyvolání (ačkoli minuta se zdá být v tomto případě poněkud přehnaná a nepravděpodobná). Pokud nenajdete žádný rozdíl, může to být jen povaha systému:nedostatečná paměť pro podporu množství dat, které se pokoušíte načíst pomocí tohoto dotazu, v kombinaci s jinými daty, která již byla ve fondu vyrovnávacích pamětí. Pokud nevěříte, že by se jednalo o problém, zjistěte, zda dvě různá provedení skutečně dávají různé plány – pokud existují nějaké rozdíly, zveřejněte plány na answer.sqlperformance.com a my se rádi podíváme . Ve skutečnosti zachycování skutečných plánů pro obě provedení pomocí Průzkumníka plánů vás v každém případě může také informovat o jakýchkoli rozdílech v I/O a může vést k tomu, že SQL Server tráví čas při prvním, pomalejším běhu.

Otázka:Získávám sniffování parametrů pomocí sp_executesql, vyřešila by to Optimalizace pro ad hoc zátěž, protože v mezipaměti je pouze útržek plánu?

A: Ne, nemyslím si, že nastavení Optimalizovat pro ad hoc zátěž tomuto scénáři pomůže, protože sniffování parametrů znamená, že následná provedení stejného plánu jsou použita pro různé parametry a s výrazně odlišným chováním při výkonu. Optimalizace pro zátěže ad hoc se používá k minimalizaci drastického dopadu na mezipaměť plánu, ke kterému může dojít, když máte vysoký počet různých příkazů SQL. Pokud tedy nemluvíte o dopadu mnoha různých příkazů na mezipaměť plánu, které posíláte do sp_executesql – což by nebylo charakterizováno jako sniffování parametrů – myslím, že experimentování s OPTION (RECOMPILE) může mít lepší výsledek, nebo pokud znáte hodnoty parametrů, které *produkují* dobré výsledky v různých kombinacích parametrů, použijte OPTIMIZE FOR . Tato odpověď od Paula Whitea může poskytnout mnohem lepší přehled.

O:Existuje způsob, jak spustit dynamické SQL a NEULOŽIT plán dotazů?

A: Jistě, stačí zahrnout OPTION (RECOMPILE) v dynamickém textu SQL:

DBCC FREEPROCCACHE;
 
USE AdventureWorks2012;
GO
SET NOCOUNT ON;
GO
 
EXEC sp_executesql 
  N'SELECT TOP (1) * INTO #x FROM Sales.SalesOrderHeader;';
GO
EXEC sp_executesql 
  N'SELECT TOP (1) * INTO #x FROM Sales.SalesOrderDetail OPTION (RECOMPILE);'
GO
 
SELECT t.[text], p.usecounts
FROM sys.dm_exec_cached_plans AS p
CROSS APPLY sys.dm_exec_sql_text(p.[plan_handle]) AS t
WHERE t.[text] LIKE N'%Sales.' + 'SalesOrder%';

Výsledky:1 řádek zobrazující Sales.SalesOrderHeader dotaz.

Nyní, pokud některý příkaz v dávce NEOBSAHUJE OPTION (RECOMPILE) , plán může být stále uložen do mezipaměti, jen jej nelze znovu použít.

O:Můžete místo toho použít BETWEEN v příkladu data z #9, pokud>=a

A: No, BETWEEN není sémanticky ekvivalentní >= AND < , ale spíše >= AND <= a optimalizuje a funguje přesně stejným způsobem. V žádném případě záměrně nepoužívám BETWEEN na dotazy na období – vždy – protože neexistuje způsob, jak z něj udělat období s otevřeným koncem. Pomocí BETWEEN , oba konce jsou inkluzivní a to může být velmi problematické v závislosti na základním datovém typu (nyní nebo kvůli nějaké budoucí změně, o které možná nevíte). Název se může zdát trochu drsný, ale v následujícím příspěvku na blogu se o tom velmi podrobně rozepíšu:

Co mají společného BETWEEN a ďábel?

O:Co v kurzoru skutečně dělá "local fast_forward"?

A: FAST_FORWARD je ve skutečnosti krátká forma READ_ONLY a FORWARD_ONLY . Zde je to, co dělají:

  • LOCAL dělá to tak, že vnější rozsahy (ve výchozím nastavení je kurzor GLOBAL pokud jste nezměnili možnost na úrovni instance).
  • READ_ONLY dělá to tak, že nemůžete aktualizovat kurzor přímo, např. pomocí WHERE CURRENT OF .
  • FORWARD_ONLY znemožňuje možnost rolování, např. pomocí FETCH PRIOR nebo FETCH ABSOLUTE místo FETCH NEXT .

Nastavení těchto možností, jak jsem předvedl (a o tom jsem napsal blog), může mít významný dopad na výkon. Velmi zřídka vidím v produkci kurzory, které se skutečně potřebují odchýlit od této sady funkcí, ale obvykle jsou napsány tak, aby akceptovaly mnohem dražší výchozí hodnoty.

O:co je efektivnější, kurzor nebo smyčka while?

A: A WHILE smyčka bude pravděpodobně efektivnější než ekvivalentní kurzor s výchozími možnostmi, ale mám podezření, že pokud použijete LOCAL FAST_FORWARD, najdete jen malý, pokud vůbec nějaký rozdíl . Obecně řečeno, WHILE loop *je* kurzor, aniž by se mu říkalo kurzor, a já jsem minulý rok vyzval některé vysoce vážené kolegy, aby mi dokázali, že se mýlím. Jejich WHILE smyčky nedopadly tak dobře.

O:Nedoporučujete předponu usp pro uživatelské uložené procedury, má to stejný negativní dopad?

A: A usp_ prefix (nebo jakýkoli jiný prefix než sp_ , nebo žádná předpona v tomto případě) *nemá* stejný dopad, jaký jsem demonstroval. V používání předpony u uložených procedur mi však připadá malý význam, protože velmi zřídka kdy pochybuji, že když najdu kód, který říká EXEC something , že něco je uložená procedura – takže tam má malou hodnotu (na rozdíl řekněme od předponování pohledů pro jejich odlišení od tabulek, protože je lze používat zaměnitelně). Pokud každé proceduře přiřadíte stejnou předponu, bude také mnohem obtížnější najít objekt, který hledáte, například v Průzkumníku objektů. Představte si, že by každé příjmení v telefonním seznamu mělo předponu LastName_ – jak vám to pomáhá?

Otázka:Existuje způsob, jak vyčistit plány uložené v mezipaměti, kde existuje více kopií?

A: Ano! Pokud používáte SQL Server 2008 nebo vyšší. Jakmile identifikujete dva plány, které jsou identické, budou mít stále samostatné plan_handle hodnoty. Identifikujte tedy ten, který si *nechcete* ponechat, zkopírujte jeho plan_handle a vložte jej do tohoto DBCC příkaz:

DBCC FREEPROCCACHE(0x06.....);
O:Způsobuje použití if else atd. v procesu špatné plány, optimalizuje se pro první spuštění a optimalizuje se pouze pro tuto cestu? Je tedy nutné z částí kódu v každém IF vytvořit samostatné procedury?

A: Protože SQL Server nyní může provádět optimalizaci na úrovni příkazů, má to dnes méně drastický účinek, než tomu bylo u starších verzí, kde musel být celý postup překompilován jako jeden celek.

O:Někdy jsem zjistil, že psaní dynamického sql může být lepší, protože eliminuje problém s čicháním parametrů pro sp. Je to pravda ? Existují nějaké kompromisy nebo jiné úvahy, které je třeba v tomto scénáři udělat?

A: Ano, dynamické SQL může často zmařit sniffování parametrů, zejména v případě, kdy masivní dotaz „kuchyňský dřez“ má spoustu volitelných parametrů. Ve výše uvedených otázkách jsem se zabýval některými dalšími úvahami.

Otázka:Pokud bych měl vypočítaný sloupec v tabulce jako DATEPART(mycolumn, year) a v něm index, použil by to SQL server s SEEK?

A: Mělo by, ale samozřejmě záleží na dotazu. Index nemusí být vhodný k pokrytí výstupních sloupců nebo k uspokojení jiných filtrů a parametr, který používáte, nemusí být dostatečně selektivní, aby ospravedlnil hledání.

Otázka:Vygeneruje se plán pro KAŽDÝ dotaz? Vytvoří se plán i pro ty triviální?

A: Pokud vím, plán se generuje pro každý platný dotaz, dokonce i triviální plány, pokud nedojde k chybě, která zabrání vygenerování plánu (to se může stát ve více scénářích, jako jsou neplatné tipy). Zda jsou uloženy do mezipaměti nebo ne (a jak dlouho v mezipaměti zůstanou), závisí na řadě dalších faktorů, z nichž některé jsem probral výše.

O:Generuje volání sp_executesql (a znovu používá) plán uložený v mezipaměti?

A: Ano, pokud odešlete přesně stejný text dotazu, nezáleží na tom, zda jej zadáte přímo nebo jej odešlete prostřednictvím sp_executesql , SQL Server uloží plán do mezipaměti a znovu jej použije.

O:Je v pořádku vynutit pravidlo (pro prostředí vývojáře), kde všechny počítače pro vývojáře používají okamžitou inicializaci souborů?

A: Nechápu proč ne. Jedinou obavu, kterou bych měl, je, že při okamžité inicializaci souboru si vývojáři nemusí všimnout velkého počtu událostí automatického růstu, což může odrážet špatné nastavení automatického růstu, které může mít velmi odlišný dopad na produkční prostředí (zejména pokud některý z těchto serverů *není * mají povoleno IFI).

O:S funkcí v klauzuli SELECT, bylo by správné říci, že je lepší kód duplikovat?

A: Osobně bych řekl, že ano. Nahrazením skalárních funkcí v SELECT jsem získal mnoho výkonnostních kilometrů seznam s inline ekvivalentem, a to i v případech, kdy musím tento kód opakovat. Jak je však uvedeno výše, v některých případech můžete zjistit, že nahrazením funkce s inline tabulkou můžete znovu použít kód bez nepříjemného snížení výkonu.

O:Můžeme použít generátory dat k získání stejné velikosti dat pro vývojové použití namísto použití (obtížně dostupných) produkčních dat? Je zkreslení dat důležité pro výsledné plány?

A: Zkreslení dat může mít určitý faktor a mám podezření, že to závisí na tom, jaký druh dat generujete/simulujete a jak daleko může být zkreslení. Pokud máte, řekněme, sloupec varchar(100), který má ve výrobě obvykle 90 znaků a vaše generování dat produkuje data, která v průměru 50 (což je to, co SQL Server předpokládá), zjistíte mnohem odlišný dopad na počet. stránek a optimalizace a pravděpodobně nepříliš realistické testy.

Ale budu upřímný:tento specifický aspekt není něco, do čeho jsem investoval mnoho času, protože si obvykle dokážu prohnat cestu k získání skutečných dat. :-)

O:Jsou všechny funkce vytvořené při zkoumání výkonu dotazu stejné? Pokud ne, existuje seznam známých funkcí, kterým byste se měli pokud možno vyhnout?

A: Ne, ne všechny funkce jsou z hlediska výkonu rovnocenné. Existují tři různé typy funkcí, které můžeme vytvořit (prozatím ignorujeme funkce CLR):

  • Skalární funkce s více příkazy
  • Funkce s vícepříkazovými tabulkami
  • Inline funkce s hodnotou tabulky
    Inline skalární funkce jsou zmíněny v dokumentaci, ale jsou mýtem a od SQL Serveru 2014 přinejmenším, může být také zmíněn vedle Sasquatche a Loch Ness Monster.

Obecně, a pokud bych mohl, řekl bych to fontem 80pt, funkce s inline tabulkou jsou dobré a ostatním by se mělo, pokud je to možné, vyhnout, protože je mnohem obtížnější je optimalizovat.

Funkce mohou mít také různé vlastnosti, které ovlivňují jejich výkon, například zda jsou deterministické a zda jsou vázané na schéma.

U mnoha funkčních vzorů je určitě potřeba zvážit výkon, a měli byste si také být vědomi této položky Connect, která je má za cíl vyřešit.

Otázka:Můžeme udržet součet bez kurzorů?

A: Ano, můžeme; existuje několik metod jiných než kurzor (jak je podrobně popsáno v mém příspěvku na blogu Nejlepší přístupy pro spouštění součtů – aktualizováno pro SQL Server 2012):

  • Poddotaz v seznamu SELECT
  • Rekurzivní CTE
  • Vlastní připojení
  • "Překvapivá aktualizace"
  • Pouze SQL Server 2012+:SUM() OVER() (použito výchozí / RANGE)
  • Pouze SQL Server 2012+:SUM() OVER() (pomocí řádků)

Poslední možnost je zdaleka nejlepší přístup, pokud používáte SQL Server 2012; pokud ne, existují omezení pro ostatní nekurzorové možnosti, které často učiní kurzor atraktivnější volbou. Například nepředvídatelná metoda aktualizace není zdokumentována a není zaručeno, že bude fungovat v očekávaném pořadí; rekurzivní CTE vyžaduje, aby v jakémkoli sekvenčním mechanismu, který používáte, nebyly žádné mezery; a přístupy poddotazu a vlastního připojení se jednoduše neškálují.


  1. Vytvoření řízení přístupu založeného na rolích v MongoDB

  2. MariaDB GROUP_CONCAT()

  3. Oracle PL/SQL:Vytvořte balíček DML online

  4. Pochopení indexů v MySQL:Část třetí