OPTION (RECOMPILE) ist immer schneller; Warum?


169

Ich bin auf eine seltsame Situation gestoßen, in der das Anhängen OPTION (RECOMPILE)an meine Abfrage dazu führt, dass sie in einer halben Sekunde ausgeführt wird. Wenn Sie sie weglassen, dauert die Abfrage weit über fünf Minuten.

Dies ist der Fall, wenn die Abfrage von Query Analyzer oder von meinem C # -Programm über ausgeführt wird SqlCommand.ExecuteReader(). Anrufen (oder nicht anrufen) DBCC FREEPROCCACHEoder DBCC dropcleanbuffersmacht keinen Unterschied; Abfrageergebnisse werden immer sofort mit OPTION (RECOMPILE)und länger als fünf Minuten ohne zurückgegeben. Die Abfrage wird immer mit denselben Parametern aufgerufen [für diesen Test].

Ich verwende SQL Server 2008.

Ich bin ziemlich OPTIONvertraut mit dem Schreiben von SQL, habe aber noch nie zuvor einen Befehl in einer Abfrage verwendet und war mit dem gesamten Konzept der Plan-Caches bis zum Scannen der Beiträge in diesem Forum nicht vertraut. Mein Verständnis aus den Beiträgen ist, dass dies OPTION (RECOMPILE)eine teure Operation ist. Es wird anscheinend eine neue Suchstrategie für die Abfrage erstellt. Warum sind nachfolgende Abfragen, bei denen das weggelassen OPTION (RECOMPILE)wird, so langsam? Sollten die nachfolgenden Abfragen nicht die Suchstrategie verwenden, die beim vorherigen Aufruf berechnet wurde und den Hinweis zur Neukompilierung enthielt?

Ist es höchst ungewöhnlich, eine Abfrage zu haben, die bei jedem einzelnen Aufruf einen Neukompilierungshinweis erfordert?

Entschuldigen Sie die Einstiegsfrage, aber ich kann nicht wirklich Kopf oder Zahl daraus machen.

UPDATE: Ich wurde gebeten, die Anfrage zu stellen ...

select acctNo,min(date) earliestDate 
from( 
    select acctNo,tradeDate as date 
    from datafeed_trans 
    where feedid=@feedID and feedDate=@feedDate 

    union 

    select acctNo,feedDate as date 
    from datafeed_money 
    where feedid=@feedID and feedDate=@feedDate 

    union 

    select acctNo,feedDate as date 
    from datafeed_jnl 
    where feedid=@feedID and feedDate=@feedDate 
)t1 
group by t1.acctNo
OPTION(RECOMPILE)

Wenn ich den Test mit Query Analyzer ausführe, stelle ich die folgenden Zeilen voran:

declare @feedID int
select @feedID=20

declare @feedDate datetime
select @feedDate='1/2/2009'

Beim Aufruf von meinem C # -Programm werden die Parameter über die SqlCommand.ParametersEigenschaft übergeben.

Für die Zwecke dieser Diskussion können Sie davon ausgehen, dass sich die Parameter niemals ändern, sodass wir einen suboptimalen Parametergeruch als Ursache ausschließen können.


3
Was sind die Parameter für die Abfrage? Schauen Sie sich diesen Artikel an. blogs.msdn.com/b/turgays/archive/2013/09/10/… Grundsätzlich versucht SQL, den Abfrageplan basierend auf Parametern zu generieren, wenn der Prozess zum ersten Mal kompiliert wird. Es kann einen Plan erzeugen, der nicht optimal ist, wenn Sie andere, möglicherweise realistischere Parameter übergeben
Sparky

3
Ist die Abfrage kurz genug, um sie hier aufzulisten? Ich denke, Sparky ist richtig und es hängt wahrscheinlich mit dem Parameter-Sniffing zusammen. Ich hatte ein ähnliches Problem, das mich bis zum Lesen dieses ausgezeichneten Artikels verwirrte
Chris

1
Aber in diesem Fall (für diesen Test) übergebe ich immer die gleichen Parameter. Keine anderen Apps konnten sich mit anderen Parametern einschleichen und die Abfrage aufrufen. Danke für die Artikel. Wird überprüfen.
Chad Decker

2
Dies kann entweder geschehen, weil die Werte der Parameter und Variablen erfasst werden, oder weil größere Vereinfachungen vorgenommen werden. Beispiele für die größeren Vereinfachungen würden kollabieren X = @X OR @X IS NULLzu X=@Xund Durchführung einer Such Sehen hier oder Prädikate weiter nach unten gegen eine Ansicht mit Fensterfunktionen drücken
Martin Smith

3
Nach Ihrer Bearbeitung werden im Query Analyzer-Beispiel Variablen und keine Parameter verwendet. Der Wert dieser wird nur mit beschnuppert RECOMPILE. Erfassen Sie auf jeden Fall die Ausführungspläne und sehen Sie sich die Unterschiede an.
Martin Smith

Antworten:


157

Es gibt Zeiten, in denen OPTION(RECOMPILE)die Verwendung Sinn macht. Nach meiner Erfahrung ist dies nur dann sinnvoll, wenn Sie dynamisches SQL verwenden. Bevor Sie untersuchen, ob dies in Ihrer Situation sinnvoll ist, würde ich empfehlen, Ihre Statistiken neu zu erstellen. Dies kann durch Ausführen der folgenden Schritte erfolgen:

EXEC sp_updatestats

Und dann erstellen Sie Ihren Ausführungsplan neu. Dadurch wird sichergestellt, dass bei der Erstellung Ihres Ausführungsplans die neuesten Informationen verwendet werden.

Hinzufügen OPTION(RECOMPILE) der Ausführungsplan jedes Mal neu erstellt, wenn Ihre Abfrage ausgeführt wird. Ich habe das noch nie gehört, creates a new lookup strategyaber vielleicht verwenden wir nur unterschiedliche Begriffe für dieselbe Sache.

Wenn eine gespeicherte Prozedur erstellt wird (ich vermute, Sie rufen Ad-hoc-SQL aus .NET auf, aber wenn Sie eine parametrisierte Abfrage verwenden, handelt es sich letztendlich um einen gespeicherten Proc-Aufruf ), versucht SQL Server, den effektivsten Ausführungsplan für diese Abfrage zu ermitteln basierend auf den Daten in Ihrer Datenbank und den übergebenen Parametern ( Parameter-Sniffing ) und diesen Plan dann zwischen. Dies bedeutet, dass der zwischengespeicherte Ausführungsplan möglicherweise nicht mehr der effektivste ist, wenn Sie die Abfrage erstellen, in der sich 10 Datensätze in Ihrer Datenbank befinden, und diese dann ausführen, wenn 100.000.000 Datensätze vorhanden sind.

Zusammenfassend - ich sehe keinen Grund dafür OPTION(RECOMPILE) der hier von Vorteil wäre. Ich vermute, Sie müssen nur Ihre Statistiken und Ihren Ausführungsplan aktualisieren. Das Wiederherstellen von Statistiken kann je nach Ihrer Situation ein wesentlicher Bestandteil der DBA-Arbeit sein. Wenn Sie nach der Aktualisierung Ihrer Statistiken immer noch Probleme haben, würde ich empfehlen, beide Ausführungspläne zu veröffentlichen.

Und um Ihre Frage zu beantworten: Ja, ich würde sagen, es ist höchst ungewöhnlich, dass Ihre beste Option darin besteht, den Ausführungsplan jedes Mal neu zu kompilieren, wenn Sie die Abfrage ausführen.


22
Ja, sp_updatestats hat es geschafft. Sie haben den Nagel auf den Kopf getroffen, als Sie eine Abfrage erwähnten, die ursprünglich für eine Tabelle mit 10 Datensätzen ausgeführt wurde, und jetzt enthält die Tabelle Millionen von Datensätzen. Das war genau mein Fall. Ich habe es in der Post nicht erwähnt, weil ich nicht dachte, dass es wichtig ist. Faszinierendes Zeug. Danke noch einmal.
Chad Decker

3
Es ist die einzige Möglichkeit, mit Tabellenvariablen zu arbeiten, da SQL immer denkt, dass es eine einzelne Zeile gibt. Wenn es mehrere tausend Zeilen enthält, wird es zu einem Problem.
Alex Zhukovskiy

4
Ein interessantes Detail: Durch das Aktualisieren von Statistiken werden alle zwischengespeicherten Pläne, die diese Statistiken verwenden, implizit ungültig, jedoch nur, wenn sich die Statistiken nach der Aktualisierungsaktion tatsächlich geändert haben . Für stark verzerrte schreibgeschützte Tabellen scheint eine explizite OPTION (RECOMPILE)Lösung die einzige zu sein.
Groo

141

Wenn es einen drastischen Unterschied zwischen Ausführungen einer Abfrage gibt, stelle ich häufig fest, dass dies oft eines von fünf Problemen ist.

  1. STATISTIKEN- Statistiken sind veraltet. Eine Datenbank speichert Statistiken über den Bereich und die Verteilung der Wertetypen in verschiedenen Spalten in Tabellen und Indizes. Dies hilft der Abfrage-Engine, einen "Angriffsplan" für die Ausführung der Abfrage zu entwickeln, z. B. die Art der Methode, mit der Schlüssel mithilfe eines Hashs zwischen Tabellen abgeglichen werden oder die gesamte Gruppe durchsucht wird. Sie können Update Statistics für die gesamte Datenbank oder nur für bestimmte Tabellen oder Indizes aufrufen. Dies verlangsamt die Abfrage von einem Lauf zum anderen, da bei veralteten Statistiken der Abfrageplan wahrscheinlich nicht optimal für die neu eingefügten oder geänderten Daten für dieselbe Abfrage ist (mehr dazu weiter unten). Es ist möglicherweise nicht richtig, Statistiken sofort in einer Produktionsdatenbank zu aktualisieren, da je nach Datenmenge, die abgetastet werden soll, ein gewisser Overhead, eine Verlangsamung und eine Verzögerung auftreten. Sie können auch einen vollständigen Scan oder eine Stichprobe verwenden, um die Statistiken zu aktualisieren. Wenn Sie sich den Abfrageplan ansehen, können Sie mit dem Befehl auch die Statistiken zu den verwendeten Indizes anzeigenDBCC SHOW_STATISTICS (Tabellenname, Indexname) . Dies zeigt Ihnen die Verteilung und Bereiche der Schlüssel, auf denen der Abfrageplan seinen Ansatz basiert.

  2. PARAMETER-SNIFFING - Der zwischengespeicherte Abfrageplan ist für die bestimmten Parameter, die Sie übergeben, nicht optimal, obwohl sich die Abfrage selbst nicht geändert hat. Wenn Sie beispielsweise einen Parameter übergeben, der nur 10 von 1.000.000 Zeilen abruft, verwendet der erstellte Abfrageplan möglicherweise einen Hash-Join. Wenn der übergebene Parameter jedoch 750.000 der 1.000.000 Zeilen verwendet, ist der erstellte Plan möglicherweise ein Index- oder Tabellenscan. In einer solchen Situation können Sie die SQL-Anweisung anweisen, die Option OPTION (RECOMPILE) oder einen SP mit WITH RECOMPILE zu verwenden. Um der Engine mitzuteilen, dass es sich um einen "Einwegplan" handelt, und um keinen zwischengespeicherten Plan zu verwenden, der wahrscheinlich nicht gilt. Es gibt keine Regel, wie diese Entscheidung getroffen werden soll. Es hängt davon ab, wie die Abfrage von den Benutzern verwendet wird.

  3. INDEXE - Möglicherweise hat sich die Abfrage nicht geändert, aber eine Änderung an anderer Stelle, z. B. das Entfernen eines sehr nützlichen Index, hat die Abfrage verlangsamt.

  4. REIHEN GEÄNDERT - Die Zeilen, die Sie abfragen, ändern sich drastisch von Anruf zu Anruf. Normalerweise werden Statistiken in diesen Fällen automatisch aktualisiert. Wenn Sie jedoch dynamisches SQL erstellen oder SQL innerhalb einer engen Schleife aufrufen, besteht die Möglichkeit, dass Sie einen veralteten Abfrageplan verwenden, der auf der falschen drastischen Anzahl von Zeilen oder Statistiken basiert. Auch in diesem Fall ist OPTION (RECOMPILE) nützlich.

  5. DIE LOGIK Es ist die Logik, Ihre Abfrage ist nicht mehr effizient, es war in Ordnung für eine kleine Anzahl von Zeilen, aber nicht mehr skalierbar. Dies beinhaltet normalerweise eine eingehendere Analyse des Abfrageplans. Zum Beispiel können Sie nicht mehr in großen Mengen arbeiten, sondern müssen Dinge aufteilen und kleinere Commits ausführen, oder Ihr Cross Product war für einen kleineren Satz in Ordnung, beansprucht jetzt jedoch CPU und Speicher, da es größer skaliert. Dies gilt möglicherweise auch für Mit DISTINCT rufen Sie für jede Zeile eine Funktion auf. Ihre Schlüsselübereinstimmungen verwenden aufgrund der CASTING-Typkonvertierung oder NULLS oder Funktionen keinen Index. Zu viele Möglichkeiten hier.

Wenn Sie eine Abfrage schreiben, sollten Sie sich im Allgemeinen ein Bild davon machen, wie bestimmte Daten in Ihrer Tabelle verteilt sind. Eine Spalte kann beispielsweise eine gleichmäßig verteilte Anzahl unterschiedlicher Werte aufweisen oder verzerrt sein. In 80% der Fälle gibt es einen bestimmten Satz von Werten, unabhängig davon, ob die Verteilung im Laufe der Zeit häufig variiert oder ziemlich statisch ist. Auf diese Weise erhalten Sie eine bessere Vorstellung davon, wie Sie eine effiziente Abfrage erstellen. Aber auch beim Debuggen haben die Abfrageleistungen eine Grundlage für die Erstellung einer Hypothese, warum sie langsam oder ineffizient ist.


2
danke Freund. Dies ist eine hervorragende Information. Ich hätte Ihre Antwort nicht verstehen können, als ich meine Frage ursprünglich gestellt habe, aber jetzt macht es für mich vollkommen Sinn.
Chad Decker

3
PARAMETER SNIFFING ist bei weitem der größte Fluch meiner Existenz. Ich wusste erst nach einer fehlgeschlagenen Interviewfrage von diesem Befehl. Meine Lösung für das Parameter-Sniffing bestand immer darin, die Parameterwerte zu hashen und "AND {hash} = {hash}" anzuhängen, sodass die SQL für verschiedene Werte immer unterschiedlich war. Ein Hack, aber es hat funktioniert.
Jeremy Boyd

27

Um der ausgezeichneten Liste (von @CodeCowboyOrg) Situationen hinzuzufügen, in denen OPTION (RECOMPILE) sehr hilfreich sein kann,

  1. Tabellenvariablen . Wenn Sie Tabellenvariablen verwenden, gibt es keine vorgefertigten Statistiken für die Tabellenvariable, was häufig zu großen Unterschieden zwischen geschätzten und tatsächlichen Zeilen im Abfrageplan führt. Die Verwendung von OPTION (RECOMPILE) für Abfragen mit Tabellenvariablen ermöglicht die Generierung eines Abfrageplans, der eine viel bessere Schätzung der beteiligten Zeilennummern enthält. Ich hatte eine besonders kritische Verwendung einer Tabellenvariablen, die unbrauchbar war und die ich aufgeben wollte, bis ich OPTION (RECOMPILE) hinzufügte. Die Laufzeit stieg von Stunden auf wenige Minuten. Das ist wahrscheinlich ungewöhnlich, aber wenn Sie Tabellenvariablen verwenden und an der Optimierung arbeiten, ist es auf jeden Fall sehenswert, ob OPTION (RECOMPILE) einen Unterschied macht.

1
Ich habe eine Abfrage mit 5 Tabellenvariablen. Auf meinem Computer läuft es länger als eine halbe Stunde. Auf dem Computer meines Kollegen wird es in <1 Sekunde ausgeführt. Die Maschinen haben ähnliche Hardware und dieselbe SQL Server-Version. Wenn wir beide OPTION (RECOMPILE) hinzufügen, wird es auf beiden Computern in 2 Sekunden ausgeführt. In allen Fällen wird der Ausführungstest in SSMS durchgeführt. Was könnte diesen Unterschied verursachen?
Adam

1
Können Sie den Ausführungsplan dafür auf Ihrem Computer und auf dem Computer Ihres Kollegen ohne Option vergleichen (neu kompilieren)? Das könnte die Ursache des Unterschieds zeigen.
DWright

1
Für temporäre Tabellen ist es die gleiche Situation?
Muflix

1
@muflix: gute frage. Ich glaube nicht, dass der Effekt für temporäre Tabellen der gleiche ist, da sie Statistiken haben und die Engine wie bei anderen Tabellen automatische Neukompilierungsentscheidungen treffen sollte, glaube ich (bin mir aber nicht sicher). Vielleicht weiß es jemand anderes mit größerer Sicherheit.
DWright

2
Die Statistiken in temporären Tabellen werden nicht automatisch aktualisiert oder neu kompiliert, daher muss der Programmierer dies tun.
J. Michael Wuerth

1

Die allererste Aktion vor dem Optimieren von Abfragen besteht darin, die Indizes und Statistiken zu defragmentieren / neu zu erstellen. Andernfalls verschwenden Sie Ihre Zeit.

Sie müssen den Ausführungsplan überprüfen, um festzustellen, ob er stabil ist (ist dasselbe, wenn Sie die Parameter ändern). Andernfalls müssen Sie möglicherweise einen Deckungsindex erstellen (in diesem Fall für jede Tabelle) (in Kenntnis des Systems können Sie einen solchen Index erstellen ist auch für andere Abfragen nützlich).

Als Beispiel: Index erstellen idx01_datafeed_trans On datafeed_trans (feedid, feedDate) INCLUDE (acctNo, tradeDate)

Wenn der Plan stabil ist oder Sie ihn stabilisieren können, können Sie den Satz mit sp_executesql ('SQL-Satz') ausführen, um einen festen Ausführungsplan zu speichern und zu verwenden.

Wenn der Plan instabil ist, müssen Sie jedes Mal eine Ad-hoc-Anweisung oder EXEC ('SQL-Satz') verwenden, um einen Ausführungsplan auszuwerten und zu erstellen. (oder eine gespeicherte Prozedur "mit Neukompilierung").

Ich hoffe es hilft.


1

Necroing diese Frage, aber es gibt eine Erklärung, die niemand in Betracht gezogen zu haben scheint.

STATISTIKEN - Statistiken sind nicht verfügbar oder irreführend

Wenn alle der folgenden Aussagen zutreffen:

  1. Die Spalten feedid und feedDate sind wahrscheinlich stark korreliert (z. B. ist eine Feed-ID spezifischer als ein Feed-Datum und der Datumsparameter ist redundante Information).
  2. Es gibt keinen Index mit beiden Spalten als sequentielle Spalten.
  3. Es gibt keine manuell erstellten Statistiken für beide Spalten.

Dann geht der SQL Server möglicherweise fälschlicherweise davon aus, dass die Spalten nicht korreliert sind, was zu niedrigeren als erwarteten Kardinalitätsschätzungen für die Anwendung beider Einschränkungen und der Auswahl eines schlechten Ausführungsplans führt. In diesem Fall besteht die Lösung darin, ein Statistikobjekt zu erstellen, das die beiden Spalten verbindet. Dies ist keine teure Operation.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.