sql >> Database >  >> RDS >> Database

Vervolg op Zomervoorstelling Palooza 2013

Op 27 juni hield het PASS Performance Virtual Chapter zijn Summer Performance Palooza 2013 - een soort verkleinde 24 Hours of PASS, maar uitsluitend gericht op prestatiegerelateerde onderwerpen. Ik gaf een sessie met de titel "10 slechte gewoonten die prestaties kunnen doden", waarbij ik de volgende 10 concepten behandelde:

  1. SELECTEER *
  2. Blinde indexen
  3. Geen schemavoorvoegsel
  4. Standaard cursoropties
  5. sp_ voorvoegsel
  6. Cache bloat toestaan
  7. Brede gegevenstypen
  8. SQL Server-standaardinstellingen
  9. Overmatig gebruik van functies
  10. "Werkt op mijn computer"

Misschien herinnert u zich enkele van deze onderwerpen uit presentaties zoals mijn "Bad Habits and Best Practices"-lezing of onze wekelijkse Query Tuning-webinars die ik van begin juni tot en met deze week met Kevin Kline heb gehost. (Die 6 video's zullen trouwens begin augustus beschikbaar zijn op YouTube.)

Mijn sessie had 351 deelnemers en ik kreeg geweldige feedback. Daar wilde ik iets over kwijt.

Ten eerste een configuratieprobleem:ik gebruikte een gloednieuwe microfoon en had geen idee dat elke toetsaanslag als onweer zou klinken. Ik heb dat probleem aangepakt met een betere plaatsing van mijn randapparatuur, maar ik wil mijn excuses aanbieden aan iedereen die hierdoor wordt getroffen.

Vervolgens de downloads; het dek en de monsters worden op de evenementensite geplaatst. Ze staan ​​onderaan de pagina, maar je kunt ze ook hier downloaden.

Tot slot, wat volgt is een lijst met vragen die tijdens de sessie zijn gepost, en ik wilde er zeker van zijn dat ik alle vragen beantwoord die niet werden beantwoord tijdens de live Q &A. Het spijt me dat ik dit in iets minder dan een maand heb geperst , maar er waren veel vragen, en ik wilde ze niet in delen publiceren.

V:Als je een proc hebt die enorm variërende invoerwaarden kan hebben voor de gegeven parameters en het resultaat is dat het gecachete plan in de meeste gevallen niet optimaal is, kun je het beste de proc maken MET RECOMPILE en de kleine nemen prestatie hit elke keer dat het wordt uitgevoerd?

A: Je zult dit van geval tot geval moeten bekijken, aangezien het echt van verschillende factoren afhangt (waaronder de complexiteit van het plan). Merk ook op dat u hercompilatie op statement-niveau kunt doen, zodat alleen de betrokken statements de treffer moeten opvangen, in tegenstelling tot de hele module. Paul White herinnerde me eraan dat mensen vaak parameters 'repareren' met RECOMPILE , maar al te vaak betekent dat 2000-stijl WITH RECOMPILE in plaats van de veel betere OPTION (RECOMPILE) , die zich niet alleen beperkt tot de instructie, maar ook parameterinbedding mogelijk maakt, die WITH RECOMPILE doet niet. Dus, als u RECOMPILE . gaat gebruiken om het snuiven van parameters te dwarsbomen, voeg je het toe aan het statement, niet aan de module.

V:Als u de optie opnieuw compileren op dynamische sql gebruikt, zou u dan een grote prestatiehit zien

A: Zoals hierboven, zal dit afhangen van de kosten en complexiteit van de plannen en er is geen manier om te zeggen:"Ja, er zal altijd een grote prestatiehit zijn." Je moet dat ook met het alternatief vergelijken.

V:Als er een geclusterde index is op de invoegdatum, gebruiken we later wanneer we gegevens ophalen de convert-functie, als we directe vergelijking gebruiken, is de querydatum niet leesbaar, in de echte wereld, wat is de betere keuze

A: Ik weet niet zeker wat "leesbaar in de echte wereld" betekent. Als u bedoelt dat u de uitvoer in een specifiek formaat wilt, kunt u meestal beter converteren naar een string aan de clientzijde. C# en de meeste andere talen die u waarschijnlijk op de presentatielaag gebruikt, zijn meer dan in staat om datum/tijd-uitvoer van de database op te maken in elk regionaal formaat dat u maar wilt.

V:Hoe bepaal je het aantal keren dat een plan in de cache wordt gebruikt - is er een kolom met die waarde of vragen op internet die deze waarde geven? Tot slot, zouden dergelijke tellingen pas relevant zijn sinds de laatste herstart?

A: De meeste DMV's zijn pas geldig sinds de laatste servicestart, en zelfs andere kunnen vaker worden gespoeld (zelfs op aanvraag - zowel per ongeluk als met opzet). De plancache is natuurlijk constant in beweging, en AFAIK-plannen die uit de cache vallen, behouden hun vorige telling niet als ze weer binnenkomen. Dus zelfs als je een plan in de cache ziet, ben ik niet 100% ervan overtuigd dat u het gebruik kunt geloven dat u vindt.

Dat gezegd hebbende, wat u waarschijnlijk zoekt, is sys.dm_exec_cached_plans.usecounts en u kunt ook sys.dm_exec_procedure_stats.execution_count vinden om informatie aan te vullen voor procedures waarbij individuele verklaringen binnen de procedures niet in de cache worden gevonden.

V:Wat zijn de problemen bij het upgraden van de db-engine naar een nieuwe versie, maar de gebruikersdatabases in oudere compatibiliteitsmodi laten staan?

A: De belangrijkste zorgen hieromtrent zijn de mogelijkheid om bepaalde syntaxis te gebruiken, zoals OUTER APPLY of variabelen met een functie met tabelwaarde. Ik ken geen gevallen waarin het gebruik van een lagere compatibiliteit een directe invloed heeft op de prestaties, maar een aantal dingen die doorgaans worden aanbevolen, zijn het opnieuw opbouwen van indexen en het bijwerken van statistieken (en om uw leverancier zo snel mogelijk het nieuwere compatibiliteitsniveau te laten ondersteunen). Ik heb in een aanzienlijk aantal gevallen onverwachte prestatievermindering zien oplossen, maar ik heb ook enkele meningen gehoord dat dit niet nodig en misschien zelfs onverstandig is.

V:Op de * maakt het uit wanneer je een bestaande clausule doet

A: Nee, in ieder geval qua prestaties, één uitzondering waarbij SELECT * maakt niet uit wanneer gebruikt in een EXISTS clausule. Maar waarom zou je * . gebruiken? hier? Ik gebruik liever EXISTS (SELECT 1 ... – de optimizer zal die hetzelfde behandelen, maar op een bepaalde manier documenteert het de code zelf en zorgt het ervoor dat lezers begrijpen dat de subquery geen gegevens retourneert (zelfs als ze de grote EXISTS missen buiten). Sommige mensen gebruiken NULL , en ik heb geen idee waarom ik 1 ben gaan gebruiken, maar ik vind NULL ook een beetje onintuïtief.

*Opmerking* je moet voorzichtig zijn als je EXISTS (SELECT *) probeert te gebruiken binnen een module die schemagebonden is:

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);

U krijgt deze foutmelding:

Msg 1054, Level 15, State 6, Procedure ThisWillNotWork, Line 6
Syntaxis '*' is niet toegestaan ​​in schemagebonden objecten.

Verander het echter in SELECT 1 werkt gewoon goed. Dus misschien is dat een ander argument om SELECT * te vermijden zelfs in dat scenario.

V:Is er een bronlink voor de beste codeerstandaarden?

A: Er zijn er waarschijnlijk honderden in verschillende talen. Net als naamgevingsconventies zijn coderingsnormen een zeer subjectief iets. Het maakt niet echt uit welke conventie u besluit het beste voor u te werken; als je van tbl houdt voorvoegsels, gek! Geef de voorkeur aan Pascal boven bigEndian, doe het maar. Wilt u uw kolomnamen vooraf laten gaan door het gegevenstype, zoals intCustomerID , ik ga je niet tegenhouden. Het belangrijkste is dat u een conventie definieert en deze *consistent gebruikt.*

Dat gezegd hebbende, als je mijn mening wilt, ik heb er geen gebrek aan.

V:Is XACT_ABORT iets dat kan worden gebruikt in SQL Server 2008 en later?

A: Ik ken geen plannen om XACT_ABORT te beëindigen dus het zou gewoon goed moeten blijven werken. Eerlijk gezegd zie ik dit niet vaak meer worden gebruikt nu we TRY / CATCH hebben (en THROW vanaf SQL Server 2012).

V:Hoe is een inline-tabelfunctie op een kruis van toepassing in vergelijking met de scalaire functie die 1.000x werd genoemd?

A: Ik heb dit niet getest, maar in veel gevallen kan het vervangen van een scalaire functie door een inline tabelwaardefunctie een grote impact hebben op de prestaties. Het probleem dat ik vind is dat het maken van deze overstap een aanzienlijke hoeveelheid werk kan zijn op een systeem dat is geschreven vóór APPLY bestond, of wordt nog steeds beheerd door mensen die deze betere aanpak niet hebben omarmd.

V:Ik heb een query die de eerste keer erg langzaam (~1 min) en om de andere keer snel (~3 seconden) wordt uitgevoerd. Waar moet ik beginnen met kijken waar het prestatieprobleem de eerste keer vandaan komt?

A: Twee dingen springen in het oog:(1) de vertraging heeft te maken met de compilatietijd of (2) de vertraging heeft te maken met de hoeveelheid gegevens die wordt geladen om aan de vraag te voldoen, en de eerste keer dat deze van schijf moet komen en niet het geheugen. Voor (1) kunt u de query uitvoeren in SQL Sentry Plan Explorer en de statusbalk toont u de compileertijd voor de eerste en volgende aanroepen (hoewel een minuut hiervoor nogal overdreven en onwaarschijnlijk lijkt). Als je geen verschil vindt, dan ligt het misschien aan de aard van het systeem:onvoldoende geheugen om de hoeveelheid gegevens te ondersteunen die je probeert te laden met deze query in combinatie met andere gegevens die al in de bufferpool zaten. Als je niet gelooft dat een van beide het probleem is, kijk dan of de twee verschillende uitvoeringen daadwerkelijk verschillende plannen opleveren - als er verschillen zijn, post de plannen dan op answer.sqlperformance.com en we zullen graag een kijkje nemen . In feite kan het vastleggen van daadwerkelijke plannen voor beide uitvoeringen met Plan Explorer u in ieder geval ook informeren over eventuele verschillen in I/O en kan ertoe leiden dat SQL Server zijn tijd besteedt aan de eerste, langzamere uitvoering.

V:Ik krijg parametersniffing met sp_executesql. Zou Optimize voor ad-hocworkloads dit oplossen omdat alleen de plan-stub zich in de cache bevindt?

A: Nee, ik denk niet dat de instelling Optimaliseren voor ad-hocworkloads dit scenario zal helpen, aangezien het snuiven van parameters impliceert dat opeenvolgende uitvoeringen van hetzelfde plan worden gebruikt voor verschillende parameters en met aanzienlijk verschillend prestatiegedrag. Optimaliseren voor ad-hocworkloads wordt gebruikt om de drastische impact op de plancache te minimaliseren die kan optreden wanneer u een groot aantal verschillende SQL-instructies hebt. Dus tenzij je het hebt over de impact op de plancache van veel verschillende instructies die je naar sp_executesql stuurt – wat niet zou worden gekarakteriseerd als het snuiven van parameters – ik denk dat ik aan het experimenteren ben met OPTION (RECOMPILE) kan een beter resultaat hebben of, als u de parameterwaarden kent die *do* goede resultaten opleveren voor verschillende parametercombinaties, gebruikt u OPTIMIZE FOR . Dit antwoord van Paul White geeft misschien een veel beter inzicht.

V:Is er een manier om dynamische SQL uit te voeren en het queryplan NIET op te slaan?

A: Natuurlijk, voeg gewoon OPTION (RECOMPILE) toe in de dynamische SQL-tekst:

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%';

Resultaten:1 rij met de Sales.SalesOrderHeader vraag.

Nu, als een instructie in de batch GEEN OPTION (RECOMPILE) bevat , het plan kan nog steeds in de cache worden bewaard, het kan alleen niet opnieuw worden gebruikt.

V:Kun je BETWEEN gebruiken op het datumvoorbeeld van #9 in plaats van als>=en

A: Nou, BETWEEN is niet semantisch gelijk aan >= AND < , maar eerder >= AND <= , en optimaliseert en presteert op exact dezelfde manier. Ik gebruik in ieder geval met opzet niet BETWEEN op datumbereikquery's - ooit - omdat er geen manier is om er een bereik met een open einde van te maken. Met BETWEEN , beide uiteinden zijn inclusief, en dit kan erg problematisch zijn, afhankelijk van het onderliggende gegevenstype (nu of vanwege een toekomstige wijziging waarvan u misschien niet op de hoogte bent). De titel lijkt misschien wat hard, maar ik ga hier uitgebreid op in in de volgende blogpost:

Wat hebben TUSSEN en de duivel gemeen?

V:Wat doet "local fast_forward" in een cursor echt?

A: FAST_FORWARD is eigenlijk de korte vorm van READ_ONLY en FORWARD_ONLY . Dit is wat ze doen:

  • LOCAL maakt het zo dat buitenbereiken (standaard is een cursor GLOBAL tenzij u de optie op instantieniveau hebt gewijzigd).
  • READ_ONLY zorgt ervoor dat u de cursor niet rechtstreeks kunt bijwerken, b.v. met behulp van WHERE CURRENT OF .
  • FORWARD_ONLY verhindert de mogelijkheid om te scrollen, b.v. met behulp van FETCH PRIOR of FETCH ABSOLUTE in plaats van FETCH NEXT .

Het instellen van deze opties, zoals ik heb aangetoond (en waarover ik heb geblogd), kan een aanzienlijke invloed hebben op de prestaties. Zeer zelden zie ik cursors in productie die daadwerkelijk moeten afwijken van deze set functies, maar meestal zijn ze toch geschreven om de veel duurdere standaardinstellingen te accepteren.

V:wat is efficiënter, een cursor of een while-lus?

A: Een WHILE loop zal waarschijnlijk efficiënter zijn dan een equivalente cursor met de standaardopties, maar ik vermoed dat je weinig of geen verschil zult vinden als je LOCAL FAST_FORWARD gebruikt . Over het algemeen geldt een WHILE loop *is* een cursor zonder een cursor te worden genoemd, en ik heb vorig jaar een aantal zeer gewaardeerde collega's uitgedaagd om mijn ongelijk te bewijzen. Hun WHILE loops deden het niet zo goed.

V:U raadt het usp-voorvoegsel niet aan voor door de gebruiker opgeslagen procedures, heeft dit dezelfde negatieve impact?

A: Een usp_ voorvoegsel (of een ander voorvoegsel dan sp_ , of geen voorvoegsel wat dat betreft) heeft *niet* dezelfde impact die ik heb aangetoond. Ik heb echter weinig waarde aan het gebruik van een voorvoegsel voor opgeslagen procedures, omdat er zelden enige twijfel over bestaat dat wanneer ik code vind die EXEC something zegt , dat iets een opgeslagen procedure is - dus daar is weinig waarde aan (in tegenstelling tot bijvoorbeeld het voorvoegsel van views om ze van tabellen te onderscheiden, omdat ze onderling uitwisselbaar kunnen worden gebruikt). Door elke procedure hetzelfde voorvoegsel te geven, wordt het ook veel moeilijker om het object te vinden dat u zoekt in bijvoorbeeld Object Explorer. Stel je voor dat elke achternaam in het telefoonboek werd voorafgegaan door LastName_ – op welke manier helpt dat jou?

V:Is er een manier om plannen in het cachegeheugen op te schonen als er meerdere exemplaren zijn?

A: Ja! Welnu, als u SQL Server 2008 of hoger gebruikt. Zodra je twee identieke plannen hebt geïdentificeerd, hebben ze nog steeds een aparte plan_handle waarden. Dus, identificeer degene die je *niet* wilt houden, kopieer de plan_handle , en plaats het in deze DBCC commando:

DBCC FREEPROCCACHE(0x06.....);
V:Veroorzaakt het gebruik van if else etc in een proc slechte plannen, wordt het geoptimaliseerd voor de eerste run en wordt het alleen geoptimaliseerd voor dat pad? Moeten de codesecties in elke IF dan worden omgezet in afzonderlijke procedures?

A: Aangezien SQL Server nu optimalisatie op instructieniveau kan uitvoeren, heeft dit tegenwoordig een minder ingrijpend effect dan op oudere versies, waar de hele procedure opnieuw als één eenheid moest worden gecompileerd.

V:Ik heb soms ontdekt dat het schrijven van dynamische SQL beter kan zijn omdat het het probleem met het snuiven van parameters voor sp elimineert. Is dit waar ? Zijn er afwegingen of andere overwegingen die gemaakt moeten worden over dit scenario?

A: Ja, dynamische SQL kan het snuiven van parameters vaak dwarsbomen, vooral in het geval dat een enorme "gootsteen"-query veel optionele parameters heeft. Ik heb enkele andere overwegingen behandeld in bovenstaande vragen.

V:Als ik een berekende kolom in mijn tabel had als DATEPART(mycolumn, year) en in index erop, zou SQL-server dit dan gebruiken met een SEEK?

A: Het zou moeten, maar het hangt natuurlijk af van de vraag. De index is mogelijk niet geschikt om de uitvoerkolommen te dekken of om aan andere filters te voldoen en de parameter die u gebruikt, is mogelijk niet selectief genoeg om een ​​zoekopdracht te rechtvaardigen.

V:wordt er voor ELKE vraag een plan gegenereerd? Wordt er zelfs voor triviale plannen een plan gemaakt?

A: Voor zover ik weet, wordt er een plan gegenereerd voor elke geldige query, zelfs triviale plannen, tenzij er een fout is die verhindert dat een plan wordt gegenereerd (dit kan in meerdere scenario's gebeuren, zoals ongeldige hints). Of ze in de cache worden opgeslagen of niet (en hoe lang ze in de cache blijven) hangt af van een aantal andere factoren, waarvan ik er enkele hierboven heb besproken.

V:genereert een aanroep naar sp_executesql (en hergebruikt) het in de cache opgeslagen plan?

A: Ja, als u exact dezelfde vraagtekst verstuurt, maakt het niet uit of u deze rechtstreeks uitgeeft of via sp_executesql verstuurt , zal SQL Server het plan cachen en hergebruiken.

V:Is het oké om een ​​regel af te dwingen (voor een ontwikkelomgeving) waarbij alle ontwikkelmachines directe bestandsinitialisatie gebruiken?

A: Ik zie niet in waarom niet. De enige zorg die ik zou hebben is dat met onmiddellijke bestandsinitialisatie de ontwikkelaars mogelijk geen grote aantallen autogrow-gebeurtenissen opmerken, wat kan wijzen op slechte autogrow-instellingen die een heel andere impact op de productieomgeving kunnen hebben (vooral als een van die servers *niet * IFI ingeschakeld hebben).

V:Zou het, met de functie in de SELECT-component, juist zijn om te zeggen dat het beter is om code te dupliceren?

A: Persoonlijk zou ik ja zeggen. Ik heb veel prestatiekilometers gehaald door het vervangen van scalaire functies in de SELECT lijst met een inline-equivalent, zelfs in gevallen waarin ik die code moet herhalen. Zoals hierboven vermeld, kan het echter zijn dat u in sommige gevallen merkt dat het vervangen van die functie door een inline-tabelwaardefunctie u codehergebruik kan opleveren zonder de vervelende prestatievermindering.

V:Kunnen we datageneratoren gebruiken om dezelfde datagrootte te krijgen voor ontwikkelingsdoeleinden in plaats van (moeilijk te verkrijgen) productiedata te gebruiken? Is de scheeftrekking van gegevens belangrijk voor de resulterende plannen?

A: Gegevensscheefheid kan een factor zijn, en ik vermoed dat het afhangt van wat voor soort gegevens u genereert / simuleert en hoe ver de scheeftrekking mogelijk is. Als u bijvoorbeeld een varchar(100)-kolom heeft die in productie doorgaans 90 tekens lang is en uw gegevensgeneratie gegevens produceert van gemiddeld 50 (wat SQL Server aanneemt), zult u een heel ander effect op het aantal vinden van pagina's en optimalisatie, en waarschijnlijk niet erg realistische tests.

Maar ik zal eerlijk zijn:dit specifieke facet is niet iets waar ik veel tijd in heb geïnvesteerd, omdat ik me meestal een weg kan banen om echte gegevens te krijgen. :-)

V:Zijn alle functies gelijk gemaakt bij het onderzoeken van queryprestaties? Zo niet, is er dan een lijst met bekende functies die u indien mogelijk moet vermijden?

A: Nee, niet alle functies zijn qua prestaties gelijk. Er zijn drie verschillende soorten functies die we kunnen creëren (waarbij we de CLR-functies voorlopig negeren):

  • Scalaire functies met meerdere instructies
  • Functies met tabelwaarde met meerdere instructies
  • Inline functies met tabelwaarde
    Inline scalaire functies worden genoemd in de documentatie, maar ze zijn een mythe en, vanaf SQL Server 2014 kan in ieder geval naast Sasquatch en het monster van Loch Ness worden genoemd.

Over het algemeen, en ik zou dat in een 80pt-lettertype plaatsen als ik kon, zijn inline tabelwaardige functies goed, en de andere moeten waar mogelijk worden vermeden, omdat ze veel moeilijker te optimaliseren zijn.

Functies kunnen ook verschillende eigenschappen hebben die hun prestaties beïnvloeden, zoals of ze deterministisch zijn en of ze schemagebonden zijn.

Voor veel functiepatronen zijn er zeker prestatie-overwegingen die u moet maken, en u moet zich ook bewust zijn van dit Connect-item dat erop gericht is deze aan te pakken.

V:Kunnen we totalen blijven bijhouden zonder cursors?

A: Ja dat kunnen we; er zijn verschillende methoden behalve een cursor (zoals beschreven in mijn blogbericht, Beste benaderingen voor het uitvoeren van totalen – bijgewerkt voor SQL Server 2012):

  • Subquery in SELECT-lijst
  • Recursieve CTE
  • Zelf lid worden
  • "Eigenzinnige update"
  • Alleen SQL Server 2012+:SUM() OVER() (met standaard / RANGE)
  • Alleen SQL Server 2012+:SUM() OVER() (met RIJEN)

De laatste optie is verreweg de beste aanpak als je SQL Server 2012 gebruikt; zo niet, dan zijn er beperkingen op de andere niet-cursoropties die een cursor vaak de aantrekkelijkere keuze maken. De eigenzinnige updatemethode is bijvoorbeeld niet gedocumenteerd en werkt niet gegarandeerd in de volgorde die u verwacht; de recursieve CTE vereist dat er geen hiaten zijn in het sequentiële mechanisme dat u gebruikt; en de subquery- en self-join-benaderingen schalen gewoon niet.


  1. Een gids voor database-automatisering met ClusterControl van verschillendenines

  2. Bulksgewijs invoegen in Oracle-database:wat is beter:VOOR Cursorlus of een eenvoudige Select?

  3. Hoe te SELECTEREN * maar zonder Kolomnamen moeten uniek zijn in elke weergave

  4. Hoe een geneste tabel te maken met behulp van door de gebruiker gedefinieerd gegevenstype in Oracle Database