Wie entferne ich einen bestimmten ungültigen Plan aus dem SQL Server-Abfragecache?

33

Wir haben eine bestimmte SQL Server 2008-Abfrage (keine gespeicherte Prozedur, sondern dieselbe SQL-Zeichenfolge - wird alle 5 Minuten ausgeführt), die zeitweise einen sehr schlechten Abfrageplan zwischenspeichert.

Diese Abfrage wird normalerweise in wenigen Millisekunden ausgeführt. Bei diesem fehlerhaften Abfrageplan dauert es jedoch mehr als 30 Sekunden.

Wie entferne ich chirurgisch nur den einen fehlerhaften zwischengespeicherten Abfrageplan aus SQL Server 2008, ohne den gesamten Abfragecache auf dem Produktionsdatenbankserver wegzublasen?

Jeff Atwood
quelle

Antworten:

39

Ich habe ein paar Dinge herausgefunden

select * from sys.dm_exec_query_stats

zeigt alle zwischengespeicherten Abfragepläne an. Leider wird dort kein SQL-Text angezeigt.

Sie können den SQL-Text jedoch wie folgt mit den Plänen verknüpfen:

select plan_handle, creation_time, last_execution_time, execution_count, qt.text
FROM 
   sys.dm_exec_query_stats qs
   CROSS APPLY sys.dm_exec_sql_text (qs.[sql_handle]) AS qt

Ab hier ist es ziemlich trivial, eine WHEREKlausel hinzuzufügen , um die SQL zu ermitteln, von der ich weiß, dass sie in der Abfrage enthalten ist, und dann kann ich Folgendes ausführen:

DBCC FREEPROCCACHE (plan_handle_id_goes_here)

um jeden Abfrageplan aus dem Abfrageplan-Cache zu entfernen. Nicht ganz einfach oder praktisch, aber es scheint zu funktionieren.

edit: das dumpen des gesamten query caches funktioniert auch und ist weniger gefährlich als es sich anhört, zumindest nach meiner erfahrung:

DBCC FREESYSTEMCACHE ('ALL') WITH MARK_IN_USE_FOR_REMOVAL;
Jeff Atwood
quelle
2
Der Ratschlag, einen Planhinweis zu verwenden, steht trotzdem.
Remus Rusanu
1
Nachdem meine Abfrage auf magische Weise aktualisiert wurde, stellte ich fest, dass es sich um einen schlechten Plan handelt. Ich plane jedoch, ihn das nächste Mal zu testen. Ein Planhinweis ist nicht hilfreich, wenn die Abfrage unter 'optional-itis' leidet. Dort enthält sie viele optionale Parameter und wurde für einen Satz optimiert. Anschließend wird sie für einen anderen Satz ausgeführt. Es gibt keinen optimalen Plan, der für diese Art von Abfrage angehängt werden kann. Es gibt einen optimalen Plan für einen Parametersatz, der wiederum für einen anderen Parametersatz schrecklich ist.
Nick.McDermaid
6

Wenn Sie wissen, wie der gute Plan aussieht, verwenden Sie einfach einen Planhinweis .

Sie können einen bestimmten Cache-Eintrag nicht entfernen, aber Sie können einen gesamten Cache-Pool mit bereinigen DBCC FREESYSTEMCACHE(cachename/poolname).

Sie können den Cache-Namen eines fehlerhaften Abfrageplans abrufen, wenn Sie über das Plan-Handle verfügen (von sys.dm_exec_requests.plan_handle für die session_id in trouble während der Ausführung oder von sys.dm_exec_query_stats nach der Ausführung):

select ce.name
from sys.dm_exec_cached_plans cp
join sys.dm_os_memory_cache_entries ce on cp.memory_object_address = ce.memory_object_address
where cp.plan_handle = @bad_plan

Alle SQL-Pläne haben jedoch den Namen "SQL-Pläne", was es schwierig macht, den richtigen für DBCC FREESYSTEMCACHE auszuwählen.

Aktualisieren

Vergessen Sie nicht DBCC FREEPROCCACHE(plan_handle), ja, das wird funktionieren.

Remus Rusanu
quelle
1
Die Möglichkeit, plan_handle an DBCC FREEPROCCACHE zu übergeben, ist in SQL Server 2008 und nicht in SQL Server 2005 verfügbar.
Mario
Was bedeutet es, wenn sys.dm_exec_cached_plansfür das plan_handlevon kein Eintrag vorhanden ist sys.dm_exec_requests?
Jonathan Gilbert
@JonathanGilbert bedeutet, dass der Plan nicht zwischengespeichert oder aus dem Cache entfernt wurde. Siehe docs.microsoft.com/en-us/sql/relational-databases/…
Remus Rusanu
Nur zur Bestätigung: Auch wenn ich gerade erst mit der Ausführung dieser Abfrage begonnen habe und die Abfrage keinen Hinweis darauf enthält, dass sie nicht zwischengespeichert werden soll, kann sie nicht zwischengespeichert werden, da SQL Server die Entscheidung getroffen hat, sie nicht zwischenzuspeichern. Es wäre nicht so, weil es noch läuft, oder? Wenn es um den Plan zu cachen entscheidet, dann würde es richtig von der Stelle im Cache gespeichert werden , an dem die Abfrage gestartet läuft?
Jonathan Gilbert
1

Die FREEPROCCACHE- Lösung ist in Ordnung, aber eine direktere Möglichkeit besteht darin, OPTION (RECOMPILE) für Ihren SQL-String zu verwenden (Sie haben erwähnt, dass es sich nicht um einen SP handelt). Dies teilt der Engine mit, dass es sich um einen Single Use-Plan handelt, da Sie dies wahrscheinlich vermuten Es gibt Parameter-Sniffing, oder Ihre Statistiken unterscheiden sich von Lauf zu Lauf erheblich, und Sie vermuten, dass es sich um ein Problem mit einem fehlerhaften Cache-Plan handelt.

DECLARE @SQL NVARCHAR(4000)
SELECT @SQL = 'SELECT * FROM Table WHERE Column LIKE @NAME OPTION (RECOMPILE)'
EXEC sp_executesql @SQL, N'@NAME varchar(15)', 'MyName' 
CodeCowboyOrg
quelle