Plötzlich langsamer Ausführungsplan für gespeicherte Prozesse


15

Ich versuche, ein Problem mit SQL Server 2000 zu verstehen. Wir sind eine Website mit moderaten Transaktionen und haben einen gespeicherten Prozess, sp_GetCurrentTransactionsder eine Kunden- ID und zwei Daten akzeptiert.

Jetzt kann diese Abfrage je nach Datum und Kunde einen Wert zwischen null und 1000 Zeilen zurückgeben.

Das Problem: Was wir erlebt haben, ist, dass wir plötzlich eine Reihe von Fehlern (normalerweise Execution Timeout Expiredoder ähnlich) für einen bestimmten Client erhalten, während sie versuchen, diesen gespeicherten Prozess auszuführen. Wir untersuchen die Abfrage, führen sie in SSMS aus und stellen fest, dass sie 30 Sekunden dauert. Also kompilieren wir den gespeicherten Prozess neu und -bang- läuft jetzt in 300ms.

Ich habe mit unserem DBA darüber gesprochen. Er hat mir gesagt, dass die Datenbank einen Abfrageplan erstellt hat, als wir den gespeicherten Prozess erstellt haben. Er sagte, es sei ein guter Plan für diesen Parametersatz, aber wenn Sie einen bestimmten Parametersatz darauf anwenden, wird der Plan nicht der beste Plan für diese Daten sein, und Sie werden sehen, dass er langsam abläuft.

Die Optionen, die mir angeboten werden, sind das Verschieben der Problemabfrage von einem gespeicherten Prozess in dynamisches SQL, dessen Ausführungsplan bei jedem Durchlauf erstellt wird.

Dies fühlt sich wie ein Schritt zurück zu mir an und ich fühle, dass es einen Weg geben muss, dies zu umgehen. Gibt es eine andere Möglichkeit, mit diesem Problem umzugehen?

Alle Antworten sind willkommen.


Gibt es eine if / else-Anweisung im proc? Ich habe gesehen, dass dies passiert, wenn der Plan in der if-Anweisung zwischengespeichert wird und dann versucht, ihn unter Verwendung des falschen Plans im else-Block auszuführen. Entsprachen diese Fehler einer Änderung im Prozess?
Jeremy Gray

@ Jeremy: Keine Änderungen am Proc und keine else / if-Anweisungen.
Ciaran Archer

Antworten:


14

Dieses Problem wird als Parameter-Sniffing bezeichnet.

In späteren Versionen von SQL Server stehen Ihnen weitere Optionen zur Verfügung, wie z. B. OPTION (RECOMPILE)oder OPTIMIZE FORHinweise.

Sie können versuchen, Variablen in der gespeicherten Prozedur zu deklarieren, den Variablen Parameterwerte zuzuweisen und die Variablen anstelle der Parameter zu verwenden, so als würden Sie die meiste Zeit einen einigermaßen zufriedenstellenden Plan erhalten.

Normalerweise sind die katastrophalsten Pläne diejenigen, die für Parameter mit sehr hoher Selektivität kompiliert wurden, jedoch mit Parametern mit geringer Selektivität ausgeführt wurden.

Angenommen, der generierte Plan ist mit diesem Ansatz robuster und für alle Parameterwerte zufriedenstellend, dann besteht der Vorteil dieses Ansatzes gegenüber dem von JNK vorgeschlagenen darin, dass nicht für jeden Aufruf Kompilierungskosten anfallen.

Der Nachteil ist, dass bei einigen Ausführungen die Laufzeit möglicherweise länger ist als bei einem speziell auf diese Parameterwerte zugeschnittenen Plan, sodass ein Kompromiss zwischen Kompilierungszeit und Ausführungszeit besteht.


3
Oder "Bind Peeking" in der Oracle-Terminologie
Gaius

Danke @ Gaius, gute Terminologie für mehr als ein RDBMS zu wissen;)
Andrei Rînea

6

Anstatt dynamisches SQL zu verwenden, können Sie Ihre Proc-Aufrufe jederzeit folgendermaßen ändern:

EXEC Database.dbo.usp_Myprocedure 'Parameter' WITH RECOMPILE

Die WITH RECOMPILETruppen (Sie haben es erraten!) Kompilieren den Ausführungsplan jedes Mal neu, wenn er ausgeführt wird.

Sie können WITH RECOMPILEin die Definition des gespeicherten Prozesses auch Folgendes einbeziehen:

CREATE PROCEDURE usp.MyProcedure (Parameters)
WITH RECOMPILE
AS
...

2

Sie könnten auch versuchen, für die Datenbank zu entscheiden, welchen Plan Sie verwenden möchten, obwohl Sie ein wenig mit dem Optimierer kämpfen würden, sodass er spröder ist, als Sie hoffen.

Die Technik ist folgende: Zerlegen Sie die gespeicherte Prozedur in zwei, eine für einen Parametersatz und eine für einen anderen. Fügen Sie jeweils where-Klauseln hinzu, sodass sie alle möglichen Fälle abdecken. Sehen Sie sich die Abfragepläne an - einer sollte für einen Parametersatz optimiert werden, der andere für den anderen. Möglicherweise müssen Sie an der Abfrage basteln, um dies zu erreichen. Andernfalls ist dies für Ihre Abfrage möglicherweise nicht möglich. In diesem Fall funktioniert dieser Ansatz nicht.

Lassen Sie nun Ihre ursprüngliche gespeicherte Prozedur die Parameterwerte überprüfen und an die entsprechende der beiden gespeicherten Prozeduren aus dem vorherigen Absatz senden.

Dies kann funktionieren, aber es ist eine Art Hack, den Optimierer zu zwingen, effektiver für Ihre Abfrage zu arbeiten. Wie bei allen derartigen Hacks könnte dies in zukünftigen Versionen der Datenbank unnötig sein oder sogar die Situation verschlimmern. Selbst wenn es funktioniert, müssen Sie sich entscheiden, ob es sich lohnt.



0

Hmmm ... wenn wir uns nur auf diese eine gespeicherte Prozedur konzentrieren, wäre ich überrascht, dass die Verwendung des zwischengespeicherten Ausführungsplans das Problem verursacht, das Sie sehen. Ich möchte den Ausführungsplan der gespeicherten Prozedur mit einer Reihe von Parametern für den Kunden und die beiden Daten anzeigen. Ich frage mich, ob ein spezifischerer Index hilfreich wäre -> wie zum Beispiel auf customerId und nur die beiden Daten?


2
Warum die Überraschung? Parameter-Sniffing ist ein recht häufiges Problem bei diesen Symptomen, und es sieht so aus, als hätte der DBA dies als Problem identifiziert.
Martin Smith

@MartinSmith - Ich bin ein wenig überrascht, dass der DBA, der sich mit Parameterschnüffeln auskennt, auch keine Hinweise zur Neukompilierung kennt ...
JNK

@ JNK - Das stimmt. Nicht sicher, warum sie das nicht erwähnen würden.
Martin Smith

0

Plötzlich nachlassende Leistung klingt nach einem ineffizienten Abfrageplan, der wahrscheinlich aufgrund fehlender Statistiken erstellt wird. Führen Sie einen SQL Server-Profiler mit den festgelegten Ereigniskategorien "Fehler und Warnungen" aus und prüfen Sie, ob Warnungen zu fehlenden Statistiken angezeigt werden.

Möglicherweise fehlt Ihnen auch ein Index, oder Sie müssen die Indizes defragmentieren, da sie möglicherweise zu fragmentiert sind, als dass SQL Server sie verwenden könnte. Dies führt zu der Annahme, dass ein Tabellenscan weniger E / A-Vorgänge verursacht.

@JNK gibt einen wichtigen Hinweis zu gespeicherten Prozessen - diese werden im Voraus kompiliert und der Abfrageplan wird zusammen mit der gespeicherten Prozedur gespeichert.

Ich bin nicht unbedingt mit WITH RECOMPILE einverstanden, da Sie dann den Vorteil verlieren, dass der Abfrageplan gespeichert und wiederverwendet wird. In einigen Fällen ist dies erforderlich - dh, wenn sich Ihre Verteilungsstatistiken in den zugrunde liegenden Tabellen zwischen den Aufrufen stark unterscheiden. Sobald die Daten in den Tabellen jedoch ausgereift sind, ändert sich die Verteilung der Daten innerhalb der Tabellen geringfügig.

Also, um zusammenzufassen:

  1. Überprüfen Sie, ob Statistiken fehlen
  2. Überprüfen Sie die Indexfragmentierung
  3. Erstellen und verwenden Sie einen gespeicherten Prozess
  4. Benennen Sie den Prozess um - sp_ ist ein vorläufig reservierter Präfix-Namespace für systeminterne SQL Server-Prozesse. Dadurch sucht SQL Server immer zuerst in der master-Datenbank nach diesen gespeicherten Prozeduren. Das Umbenennen von proc usp_ anstelle von sp_ führt zu einer Leistungssteigerung, aber ich bezweifle, dass dies Ihr Problem ist.
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.