Ich habe einen typischen Fall, in dem Parameter-Sniffing dazu führt, dass ein "fehlerhafter" Ausführungsplan im Plan-Cache landet und nachfolgende Ausführungen meiner gespeicherten Prozedur sehr langsam sind. Ich kann dieses Problem mit lokalen Variablen "lösen" OPTIMIZE FOR ... UNKNOWN
, und OPTION(RECOMPILE)
. Ich kann mich jedoch auch mit der Abfrage befassen und versuchen, sie zu optimieren.
Ich versuche herauszufinden, ob ich es sollte : Angesichts der begrenzten Zeit, um Probleme zu beheben, würde ich gerne wissen, welche Kosten es kostet, dies nicht zu tun. Aus meiner Sicht OPTION(RECOMPILE)
besteht der Nettoeffekt darin, dass ein Abfrageplan jedes Mal neu erstellt wird, wenn die Abfrage ausgeführt wird. Also, ich denke ich muss wissen:
Wie können Sie die Kosten für die Erstellung eines Abfrageplans ermitteln?
Um meine eigene Frage zu beantworten, habe ich gegoogelt (z. B. mit dieser Abfrage ) und die Dokumentation der Spalten für die dm_exec_query_stats
DMV durchgesehen . Ich habe auch das Ausgabefenster in SSMS nach "Actual Query Plan" durchsucht, um diese Informationen zu finden. Schließlich habe ich DBA.SE gesucht . Keiner von diesen führte zu einer Antwort.
Kann mir jemand sagen? Ist es möglich, die für die Planerstellung benötigte Zeit zu finden oder zu messen?
quelle
Antworten:
Sie können die Eigenschaften des Stammknotens im Abfrageplan anzeigen, zum Beispiel:
(Screenshot aus dem kostenlosen Sentry One Plan Explorer )
Diese Informationen stehen auch zur Verfügung, indem Sie den Plan-Cache abfragen, z. B. mithilfe einer Abfrage, die auf den folgenden Beziehungen basiert:
Eine vollständige Beschreibung der Optionen, die Sie zur Bearbeitung dieser Art von Abfragen haben, finden Sie im kürzlich aktualisierten Artikel von Erland Sommarskog .
quelle
Angenommen, "Kosten" sind in Bezug auf die Zeit (obwohl Sie nicht sicher sind, was es sonst in Bezug auf ;-) sein könnte, sollten Sie zumindest in der Lage sein, sich ein Bild davon zu machen, indem Sie Folgendes tun:
Der erste auf der Registerkarte "Nachrichten" gemeldete Artikel sollte sein:
Ich würde dies mindestens 10 Mal ausführen und sowohl die "CPU" - als auch die "verstrichene" Millisekunde mitteln.
Im Idealfall würden Sie dies in der Produktion ausführen, um eine echte Zeitschätzung zu erhalten, aber nur selten dürfen Benutzer den Plan-Cache in der Produktion leeren. Glücklicherweise wurde es ab SQL Server 2008 möglich, einen bestimmten Plan aus dem Cache zu löschen. In diesem Fall können Sie Folgendes tun:
Abhängig von der Variabilität der Werte, die für die Parameter übergeben werden, die den "fehlerhaften" zwischengespeicherten Plan verursachen, ist jedoch eine andere Methode zu berücksichtigen, die einen Mittelweg zwischen
OPTION(RECOMPILE)
und darstelltOPTION(OPTIMIZE FOR UNKNOWN)
: Dynamic SQL. Ja, ich habe es gesagt. Und ich meine sogar nicht parametrisiertes dynamisches SQL. Hier ist warum.Sie haben eindeutig Daten mit einer ungleichmäßigen Verteilung, zumindest in Bezug auf einen oder mehrere Eingabeparameterwerte. Die Nachteile der genannten Optionen sind:
OPTION(RECOMPILE)
Für jede Ausführung wird ein Plan erstellt, und Sie können niemals von einer Wiederverwendung des Plans profitieren , selbst wenn die erneut übergebenen Parameterwerte mit den vorherigen Läufen identisch sind. Bei Prozessen, die häufig aufgerufen werden - mindestens alle paar Sekunden -, werden Sie vor der gelegentlichen schrecklichen Situation bewahrt, bleiben jedoch in einer immer nicht allzu großartigen Situation.OPTION(OPTIMIZE FOR (@Param = value))
wird einen Plan erstellen, der auf diesem bestimmten Wert basiert. Dies könnte in mehreren Fällen hilfreich sein, lässt Sie jedoch weiterhin für das aktuelle Problem offen.OPTION(OPTIMIZE FOR UNKNOWN)
Es wird ein Plan erstellt, der auf einer durchschnittlichen Verteilung basiert, die einigen Abfragen hilft, anderen jedoch schadet. Dies sollte mit der Option zur Verwendung lokaler Variablen identisch sein.Bei korrekter Ausführung von Dynamic SQL haben die verschiedenen übergebenen Werte jedoch ihre eigenen, idealen Abfragepläne (so gut sie auch sein werden). Die Hauptkosten hierbei sind, dass mit zunehmender Anzahl der übergebenen Werte die Anzahl der Ausführungspläne im Cache zunimmt und sie Speicher belegen. Die Nebenkosten sind:
String-Parameter müssen validiert werden, um SQL-Injections zu verhindern
Möglicherweise müssen Sie ein Zertifikat und einen zertifikatbasierten Benutzer einrichten, um die ideale Sicherheitsabstraktion aufrechtzuerhalten, da für Dynamic SQL direkte Tabellenberechtigungen erforderlich sind.
So habe ich diese Situation gemeistert, als ich Procs hatte, die mehr als einmal pro Sekunde aufgerufen wurden und mehrere Tabellen mit jeweils Millionen von Zeilen trafen. Ich hatte es versucht,
OPTION(RECOMPILE)
aber dies erwies sich in 99% der Fälle, in denen der Parameter Sniffing / Bad-Cached-Plan-Problem nicht auftrat, als viel zu schädlich für den Prozess. Beachten Sie bitte, dass in einem dieser Prozesse ungefähr 15 Abfragen enthalten waren und nur 3 bis 5 davon wie hier beschrieben in dynamisches SQL konvertiert wurden. Dynamisches SQL wurde nur verwendet, wenn es für eine bestimmte Abfrage erforderlich war.Wenn die gespeicherte Prozedur mehrere Eingabeparameter enthält, ermitteln Sie, welche mit Spalten mit sehr unterschiedlichen Datenverteilungen verwendet werden (und daher dieses Problem verursachen) und welche mit Spalten mit gleichmäßigeren Verteilungen verwendet werden (und nicht sollten) dieses Problem verursachen).
Erstellen Sie die Dynamic SQL-Zeichenfolge mit Parametern für die Proc-Eingabeparameter, die gleichmäßig verteilten Spalten zugeordnet sind. Diese Parametrisierung trägt dazu bei, die resultierende Zunahme der Ausführungspläne im Cache für diese Abfrage zu reduzieren.
Für die übrigen Parameter, die mit sehr unterschiedlichen Verteilungen verknüpft sind, sollten diese als Literalwerte in Dynamic SQL verkettet werden. Da eine eindeutige Abfrage durch Änderungen am Abfragetext bestimmt wird,
WHERE StatusID = 1
ist having eine andere Abfrage und damit ein anderer Abfrageplan als havingWHERE StatusID = 2
.Handelt es sich bei einem der Proc-Eingabeparameter, die in den Text der Abfrage eingebunden werden sollen, um Zeichenfolgen, müssen diese zum Schutz vor SQL Injection validiert werden (dies ist jedoch weniger wahrscheinlich, wenn die übergebenen Zeichenfolgen vom generiert werden App und kein Benutzer, aber immer noch). Zumindest tun Sie dies,
REPLACE(@Param, '''', '''''')
um sicherzustellen, dass einfache Anführungszeichen zu einfachen Anführungszeichen werden.Erstellen Sie bei Bedarf ein Zertifikat, das zum Erstellen eines Benutzers verwendet wird, und signieren Sie die gespeicherte Prozedur so, dass direkte Tabellenberechtigungen nur dem neuen zertifikatbasierten Benutzer und nicht
[public]
oder nur Benutzern gewährt werden, die ansonsten nicht über solche Berechtigungen verfügen sollten .Beispiel proc:
quelle
OPTION
würde ; zumindest mehr als nur eine Antwort auf meine Abfrage) und würde mich nicht allzu sehr verletzen, da dieser Sproc in Integrationstests gut genutzt wird. - Auf jeden Fall: Danke für deine Einsichten!