sql >> Databasteknik >  >> RDS >> Database

Uppföljning av Summer Performance Palooza 2013

Den 27 juni höll PASS Performance Virtual Chapter sin 2013 Summer Performance Palooza – en sorts nedskalad 24 Hours of PASS, men fokuserade enbart på prestationsrelaterade ämnen. Jag höll en session med titeln "10 dåliga vanor som kan döda prestation", och behandlade följande 10 koncept:

  1. VÄLJ *
  2. Blinda index
  3. Inget schemaprefix
  4. Standardmarköralternativ
  5. sp_ prefix
  6. Tillåter uppblåst cache
  7. Brett datatyper
  8. SQL-serverns standardinställningar
  9. Överanvändning av funktioner
  10. "Fungerar på min maskin"

Du kanske kommer ihåg några av dessa ämnen från sådana presentationer som mitt "Dåliga vanor och bästa praxis"-föreläsning eller våra veckovisa webbseminarier för Query Tuning som jag har varit värd för med Kevin Kline från början av juni till denna vecka. (Dessa 6 videor kommer förresten att finnas tillgängliga i början av augusti på YouTube.)

Min session hade 351 deltagare och jag fick bra feedback. Jag ville ta upp en del av det.

Först ett konfigurationsproblem:jag använde en helt ny mikrofon och hade ingen aning om att varje knapptryckning skulle låta som åska. Jag har åtgärdat det problemet med bättre placering av min kringutrustning, men jag vill be alla som påverkas av det om ursäkt.

Nästa, nedladdningarna; däcket och proverna läggs upp på evenemangsplatsen. De finns längst ner på sidan, men du kan också ladda ner dem här.

Till sist, vad som följer är en lista över frågor som postades under sessionen, och jag ville försäkra mig om att jag tog upp alla som inte besvarades under live Q &A. Jag ber om ursäkt för att jag har klämt in detta på knappt en månad , men det fanns många frågor, och jag ville inte publicera dem i delar.

F:Om du har en proc som kan ha väldigt varierande indatavärden för de givna parametrarna och resultatet är att den cachade planen inte är optimal för de flesta fall, är det bäst att skapa proc MED RECOMPILE och ta den lilla prestationsträff varje gång den körs?

Svar: Du måste närma dig detta från fall till fall, eftersom det verkligen kommer att bero på en mängd olika faktorer (inklusive planens komplexitet). Observera också att du kan göra omkompilering på satsnivå så att endast de berörda satserna måste ta träffen, i motsats till hela modulen. Paul White påminde mig om att folk ofta "fixar" parametersniffning med RECOMPILE , men alltför ofta betyder det 2000-stil WITH RECOMPILE snarare än det mycket bättre OPTION (RECOMPILE) , som inte bara begränsar sig till satsen, utan även möjliggör parameterinbäddning, som WITH RECOMPILE gör inte. Så om du ska använda RECOMPILE för att förhindra parametersniffning, lägg till det i satsen, inte modulen.

F:Om du använder alternativet omkompilera på dynamisk sql skulle du se en stor prestandaträff

Svar: Som ovan kommer detta att bero på kostnaden och komplexiteten i planerna och det finns inget sätt att säga, "Ja, det kommer alltid att finnas en stor prestationsträff." Du måste också se till att jämföra det med alternativet.

F:Om det finns ett klustrat index på insertdate, senare när vi hämtar data, använder vi konverteringsfunktionen, om vi använder direkt jämförelse är frågedatumet inte läsbart, i verkligheten, vad är det bättre valet

Svar: Jag är inte säker på vad "läsbar i verkligheten" betyder. Om du menar att du vill ha utdata i ett specifikt format är det vanligtvis bättre att konvertera till en sträng på klientsidan. C# och de flesta andra språk som du förmodligen använder i presentationsnivån är mer än kapabla att formatera datum/tid från databasen i vilket regionalt format du vill.

F:Hur bestämmer man hur många gånger en cachad plan används – finns det en kolumn med det värdet eller några frågor på internet som ger detta värde? Slutligen, skulle sådana räkningar endast vara relevanta sedan den senaste omstarten?

Svar: De flesta DMV:er är endast giltiga sedan den senaste servicestarten, och även andra kan spolas oftare (även på begäran – både av misstag och med avsikt). Plancachen är naturligtvis i konstant förändring, och AFAIK-planer som faller ur cachen behåller inte sin tidigare räkning om de dyker in igen. Så även när du ser en plan i cachen är jag inte 100% säker på att du kan tro på användningstalet du hittar.

Som sagt, det du förmodligen letar efter är sys.dm_exec_cached_plans.usecounts och du kan också hitta sys.dm_exec_procedure_stats.execution_count för att komplettera informationen för procedurer där enskilda uttalanden inom procedurerna inte finns i cachen.

F:Vilka är problemen när du uppgraderar db-motorn till en ny version men lämnar användardatabaserna i äldre kompatibilitetslägen?

Svar: De största problemen kring detta är möjligheten att använda viss syntax, såsom OUTER APPLY eller variabler med en tabellvärderad funktion. Jag är inte medveten om några fall där användning av en lägre kompatibilitet har någon direkt inverkan på prestanda, men ett par saker som vanligtvis rekommenderas är att bygga om index och uppdatera statistik (och att få din leverantör att stödja den nyare kompatibilitetsnivån ASAP). Jag har sett det lösa oväntad prestandaförsämring i ett stort antal fall, men jag har också hört några åsikter om att det inte är nödvändigt och kanske till och med oklokt.

F:På * spelar det någon roll när man gör en existens-sats

Svar: Nej, åtminstone när det gäller prestanda, ett undantag där SELECT * spelar ingen roll när den används i en EXISTS klausul. Men varför skulle du använda * här? Jag föredrar att använda EXISTS (SELECT 1 ...). – optimeraren kommer att behandla dem på samma sätt, men på ett sätt dokumenterar den själv koden och säkerställer att läsarna förstår att underfrågan inte returnerar någon data (även om de missar den stora EXISTS utanför). Vissa människor använder NULL , och jag har ingen aning om varför jag började använda 1, men jag hittar NULL lite ointuitivt också.

*Obs* du måste vara försiktig om du försöker använda EXISTS (SELECT * inuti en modul som är schemabunden:

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

Du får det här felet:

Msg 1054, Nivå 15, Tillstånd 6, Procedur ThisWillNotWork, Rad 6
Syntax '*' är inte tillåten i schemabundna objekt.

Ändrar dock den till SELECT 1 fungerar bra. Så det kanske är ytterligare ett argument för att undvika SELECT * även i det scenariot.

F:Finns det någon resurslänk för bästa kodningsstandarder?

Svar: Det finns förmodligen hundratals över en mängd olika språk. Liksom namnkonventioner är kodningsstandarder en mycket subjektiv sak. Det spelar egentligen ingen roll vilken konvention du bestämmer dig för fungerar bäst för dig; om du gillar tbl prefix, bli galen! Föredrar Pascal framför bigEndian, ha det. Vill du prefixa dina kolumnnamn med datatypen, som intCustomerID , jag tänker inte stoppa dig. Det viktigaste är att du definierar en konvention och använder den *konsekvent.*

Som sagt, om du vill ha mina åsikter har jag ingen brist på dem.

F:Är XACT_ABORT något som kan användas i SQL Server 2008 och framåt?

Svar: Jag känner inte till några planer på att fasa ut XACT_ABORT så det borde fortsätta fungera bra. Ärligt talat ser jag inte att detta används så ofta nu när vi har TRY / CATCH (och THROW från och med SQL Server 2012).

F:Hur är en inline-tabellfunktion på ett kors tillämplig jämfört med den skalära funktionen som kallades 1 000x?

Svar: Jag testade inte detta, men i många fall kan det ha stor inverkan på prestandan att ersätta en skalär funktion med en inline-tabellvärderad funktion. Problemet jag upptäcker är att att göra den här växlingen kan vara en stor mängd arbete på systemet som skrevs innan APPLY existerade, eller fortfarande hanteras av folk som inte har anammat detta bättre tillvägagångssätt.

F:Jag har en fråga som går väldigt långsamt första gången (~1 min) och snabbt (~3 sekunder) varannan gång. Var ska jag börja titta på var prestandaproblemet kommer ifrån första gången?

Svar: Två saker hoppar på:(1) fördröjningen har att göra med kompileringstiden eller (2) fördröjningen har att göra med mängden data som laddas för att tillfredsställa frågan, och första gången den måste komma från disken och inte minne. För (1) kan du köra frågan i SQL Sentry Plan Explorer och statusfältet visar dig kompileringstid för de första och efterföljande anropen (även om en minut verkar ganska överdriven för detta, och osannolikt). Om du inte hittar någon skillnad kan det bara vara systemets natur:otillräckligt minne för att stödja mängden data du försöker ladda med den här frågan i kombination med annan data som redan fanns i buffertpoolen. Om du inte tror att något av dessa är problemet, se om de två olika avrättningarna faktiskt ger olika planer – om det finns några skillnader, lägg upp planerna på answers.sqlperformance.com så tar vi gärna en titt . Faktum är att fånga faktiska planer för båda körningarna med Plan Explorer i alla fall kan också informera dig om eventuella skillnader i I/O och kan leda till var SQL Server spenderar sin tid på den första, långsammare körningen.

Fråga:Jag får parametersniffning med sp_executesql, skulle Optimize för ad hoc-arbetsbelastningar lösa detta eftersom bara planstubben finns i cachen?

Svar: Nej, jag tror inte att inställningen Optimera för ad hoc-arbetsbelastningar kommer att hjälpa detta scenario, eftersom parametersniffning innebär att efterföljande körningar av samma plan används för olika parametrar och med signifikant olika prestandabeteenden. Optimera för ad hoc-arbetsbelastningar används för att minimera den drastiska inverkan på planens cache som kan inträffa när du har ett stort antal olika SQL-satser. Så om du inte talar om påverkan på plancachen för många olika uttalanden som du skickar till sp_executesql – som inte skulle karakteriseras som parametersniffning – jag tror att experimentera med OPTION (RECOMPILE) kan ha ett bättre resultat eller, om du känner till de parametervärden som *gör* ger bra resultat över en mängd olika parameterkombinationer, använd OPTIMIZE FOR . Detta svar från Paul White kan ge mycket bättre insikt.

F:Finns det något sätt att köra dynamisk SQL och INTE spara frågeplanen?

Svar: Visst, inkludera bara OPTION (RECOMPILE) i den dynamiska SQL-texten:

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

Resultat:1 rad som visar Sales.SalesOrderHeader fråga.

Nu, om någon sats i partiet INTE inkluderar OPTION (RECOMPILE) , planen kan fortfarande vara cachad, den kan bara inte återanvändas.

F:Kan du använda BETWEEN på datumexemplet från #9 istället om>=och

Svar: Tja, BETWEEN är inte semantiskt ekvivalent med >= AND < utan snarare >= AND <= , och optimerar och presterar på exakt samma sätt. I alla fall använder jag inte BETWEEN med avsikt på datumintervallfrågor – någonsin – eftersom det inte finns något sätt att göra det till ett öppet intervall. Med BETWEEN , båda ändarna är inklusive, och detta kan vara mycket problematiskt beroende på den underliggande datatypen (nu eller på grund av någon framtida förändring som du kanske inte känner till). Rubriken kan tyckas lite hård, men jag går in i detalj om detta i följande blogginlägg:

Vad har BETWEEN och djävulen gemensamt?

F:Vad gör "local fast_forward" i en markör egentligen?

Svar: FAST_FORWARD är faktiskt den korta formen av READ_ONLY och FORWARD_ONLY . Så här gör de:

  • LOCAL gör det så att yttre omfång (som standard är en markör GLOBAL). om du inte har ändrat alternativet på instansnivå).
  • READ_ONLY gör det så att du inte kan uppdatera markören direkt, t.ex. med WHERE CURRENT OF .
  • FORWARD_ONLY förhindrar möjligheten att rulla, t.ex. med FETCH PRIOR eller FETCH ABSOLUTE istället för FETCH NEXT .

Att ställa in dessa alternativ, som jag visade (och har bloggat om), kan ha en betydande inverkan på prestandan. Mycket sällan ser jag markörer i produktionen som faktiskt behöver avvika från denna uppsättning funktioner, men vanligtvis är de skrivna för att acceptera de mycket dyrare standardinställningarna ändå.

F:vad är mer effektivt, en markör eller en while-loop?

Svar: En WHILE loop kommer förmodligen att vara effektivare än en motsvarande markör med standardalternativen, men jag misstänker att du kommer att hitta liten om någon skillnad om du använder LOCAL FAST_FORWARD . Generellt sett en WHILE loop *är* en markör utan att kallas markör, och jag utmanade några högt uppskattade kollegor att bevisa att jag har fel förra året. Deras WHILE loopar gick inte så bra.

F:Du rekommenderar inte usp-prefix för användarlagrade procedurer, har detta samma negativa inverkan?

Svar: En usp_ prefix (eller något annat prefix än sp_ , eller inget prefix för den delen) *inte* har samma effekt som jag visade. Jag finner dock lite värde i att använda ett prefix på lagrade procedurer eftersom det mycket sällan råder någon tvekan om att när jag hittar kod som säger EXEC something , att något är en lagrad procedur – så det finns lite värde där (till skillnad från till exempel prefixvyer för att skilja dem från tabeller, eftersom de kan användas omväxlande). Att ge varje procedur samma prefix gör det också så mycket svårare att hitta objektet du är ute efter i, till exempel, Object Explorer. Tänk dig om alla efternamn i telefonboken hade prefixet LastName_ – på vilket sätt hjälper det dig?

F:Finns det något sätt att rensa upp cachade planer där det finns flera kopior?

Svar: ja! Tja, om du använder SQL Server 2008 eller senare. När du har identifierat två planer som är identiska kommer de fortfarande att ha separata plan_handle värden. Så identifiera den du *inte* vill behålla, kopiera dess plan_handle , och placera den i denna DBCC kommando:

DBCC FREEPROCCACHE(0x06.....);
F:Orsakar användningen av if else etc i en proc dåliga planer, optimeras den för första körningen och optimeras bara för den vägen? Så behöver kodavsnitten i varje IF göras till separata procedurer?

Svar: Eftersom SQL Server nu kan utföra optimering på satsnivå, har detta en mindre drastisk effekt idag än på äldre versioner, där hela proceduren måste kompileras om till en enhet.

F:Jag upptäckte ibland att det kan vara bättre att skriva dynamisk sql eftersom det eliminerar problem med parametersniffning för sp. Är detta sant ? Finns det avvägningar eller andra överväganden att göra kring detta scenario?

Svar: Ja, dynamisk SQL kan ofta omintetgöra parametersniffning, särskilt i de fall där en massiv "köksvask"-fråga har många valfria parametrar. Jag behandlade några andra överväganden i frågorna ovan.

F:Om jag hade en beräknad kolumn i min tabell som DATEPART(minkolumn, år) och i index på den, skulle SQL-servern använda detta med en SEEK?

Svar: Det borde det, men det beror förstås på frågan. Indexet kanske inte är lämpligt för att täcka utdatakolumnerna eller uppfylla andra filter och parametern du använder kanske inte är tillräckligt selektiv för att motivera en sökning.

F:genereras en plan för VARJE fråga? Skapas en plan även för triviala sådana?

Svar: Så vitt jag vet genereras en plan för varje giltig fråga, även triviala planer, såvida det inte finns ett fel som förhindrar att en plan genereras (detta kan hända i flera scenarier, till exempel ogiltiga tips). Huruvida de är cachelagrade eller inte (och hur länge de stannar i cachen) beror på en mängd andra faktorer, av vilka några jag har diskuterat ovan.

F:Genererar (och återanvänder) ett anrop till sp_executesql den cachade planen?

Svar: Ja, om du skickar exakt samma frågetext spelar det ingen roll om du skickar den direkt eller skickar den via sp_executesql , kommer SQL Server att cache och återanvända planen.

F:Är det ok att tillämpa en regel (för en dev-miljö) där alla dev-maskiner använder omedelbar filinitiering?

Svar: Jag förstår inte varför inte. Den enda oro jag skulle ha är att med omedelbar filinitiering kanske utvecklarna inte märker ett stort antal autotillväxthändelser, vilket kan återspegla dåliga autotillväxtinställningar som kan ha en helt annan inverkan på produktionsmiljön (speciellt om någon av dessa servrar *inte gör det) * har IFI aktiverat).

F:Med funktionen i SELECT-satsen, skulle det då vara korrekt att säga att det är bättre att duplicera kod?

Svar: Personligen skulle jag säga ja. Jag har fått mycket prestanda av att ersätta skalära funktioner i SELECT lista med en inline-motsvarighet, även i de fall jag måste upprepa den koden. Som nämnts ovan kan du dock i vissa fall upptäcka att om du ersätter den med en inline-tabellvärderad funktion kan du återanvända kod utan den otäcka prestationsstraffen.

F:Kan vi använda datageneratorer för att få samma datastorlek för utvecklingsanvändning istället för att använda (svårt att få tag på) produktionsdata? Är dataskevheten viktig för de resulterande planerna?

Svar: Dataskevning kan ha en faktor, och jag misstänker att det beror på vilken typ av data du genererar/simulerar och hur långt borta skevningen kan vara. Om du till exempel har en varchar(100)-kolumn som i produktion är typiskt 90 tecken lång och din datagenerering producerar data som i genomsnitt är 50 (vilket är vad SQL Server kommer att anta), kommer du att hitta mycket olika inverkan på antalet av sidor och optimering, och förmodligen inte särskilt realistiska tester.

Men jag ska vara ärlig:den här specifika aspekten är inget jag har investerat mycket tid i, eftersom jag vanligtvis kan mobba mig till att få riktig data. :-)

F:Är alla funktioner skapade lika när man undersöker frågeprestanda? Om inte, finns det en lista över kända funktioner som du bör undvika när det är möjligt?

Svar: Nej, alla funktioner är inte skapade lika prestandamässigt. Det finns tre olika typer av funktioner som vi kan skapa (som ignorerar CLR-funktioner för närvarande):

  • Skalära funktioner med flera påståenden
  • Funktioner med tabellvärde med flera påståenden
  • Inline tabellvärderade funktioner
    Inline skalära funktioner nämns i dokumentationen, men de är en myt och, från och med SQL Server Åtminstone 2014 kan lika gärna nämnas tillsammans med Sasquatch och Loch Ness-monstret.

I allmänhet, och jag skulle lägga det i 80 pkts teckensnitt om jag kunde, är inline-tabellvärderade funktioner bra, och de andra bör undvikas när det är möjligt, eftersom de är mycket svårare att optimera.

Funktioner kan också ha olika egenskaper som påverkar deras prestanda, till exempel om de är deterministiska och om de är schemabundna.

För många funktionsmönster finns det definitivt prestandaöverväganden du måste göra, och du bör också vara medveten om detta Connect-objekt som syftar till att ta itu med dem.

F:Kan vi fortsätta köra totaler utan markörer?

Svar: Ja det kan vi; det finns flera andra metoder än en markör (som beskrivs i mitt blogginlägg, Bästa metoder för att köra totaler – uppdaterad för SQL Server 2012):

  • Underfråga i SELECT-listan
  • Rekursiv CTE
  • Gå med själv
  • "Fantastisk uppdatering"
  • Endast SQL Server 2012+:SUM() OVER() (med standard / RANGE)
  • Endast SQL Server 2012+:SUM() OVER() (med ROWS)

Det sista alternativet är den absolut bästa metoden om du använder SQL Server 2012; Om inte finns det restriktioner för andra alternativ som inte är markörer som ofta gör en markör till det mer attraktiva valet. Till exempel är den knäppa uppdateringsmetoden odokumenterad och inte garanterad att fungera i den ordning du förväntar dig; den rekursiva CTE kräver att det inte finns några luckor i vilken sekventiell mekanism du än använder; och tillvägagångssätten för underfrågan och självanslutning skalas helt enkelt inte.


  1. Varför får jag OutOfRange Exception i GetOrdinal Function i detta CLOB-fält?

  2. Lös problem med SQL Server-databas som fastnat i misstänkt läge effektivt

  3. Varför skulle Oracle.ManagedDataAccess inte fungera när Oracle.DataAccess gör det?

  4. Varför är SELECT utan kolumner giltigt