Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

SQL Server-Interna:Plan Caching Pt. II – Pläne neu zusammenstellen

Dies ist Teil einer Reihe von SQL Server Internals Plan Caching. Lesen Sie unbedingt Kalens ersten Beitrag zu diesem Thema.

SQL Server gibt es seit über 30 Jahren und ich arbeite fast genauso lange mit SQL Server. Ich habe im Laufe der Jahre (und Jahrzehnte!) und Versionen dieses unglaublichen Produkts viele Veränderungen gesehen. In diesen Beiträgen werde ich mit Ihnen teilen, wie ich einige der Funktionen oder Aspekte von SQL Server betrachte, manchmal zusammen mit einem kleinen historischen Blickwinkel

In meinem vorherigen Artikel , habe ich über die SQL Server-Diagnose gesprochen, einschließlich der verschiedenen Optionen, die SQL Server für die Wiederverwendung eines Abfrageplans bietet. Wir haben uns drei Arten von Abfrageplänen angesehen:Ad-hoc, vorbereitet und Prozedur. Ich beendete die Diskussion mit einem Blick auf eine unangemessene Wiederverwendung eines Plans, die passieren kann, wenn SQL Server Parameter-Sniffing in den falschen Situationen anwendet. Wenn ein Plan auf einem Anfangswert basiert, der dazu führt, dass der Optimierer einen für diesen Wert geeigneten Plan generiert, und dann derselbe Plan für einen anderen Wert verwendet wird, ist der Plan möglicherweise nicht mehr optimal.

Was können wir also tun, wenn Parameter-Sniffing ein Problem darstellt? Wir können SQL Server dazu zwingen, einen neuen Plan zu erstellen. Normalerweise nennen wir das Erstellen eines neuen Plans „Neukompilieren“, aber es sollte wahrscheinlich „Neuoptimierung“ genannt werden. Die meisten Leute verwenden jedoch den Begriff „neu kompilieren“, also werde ich ihn hier verwenden.

Wenn die unangemessene Verwendung von Parameter-Sniffing ein Problem darstellt, besteht eine einfache Lösung darin, SQL Server einfach anzuweisen, einen neuen Plan zu erstellen. Für einzelne Anweisungen, z. B. bei automatisch parametrisierten PREPARED-Plänen, können wir den RECOMPILE-Hinweis zu einer Abfrage hinzufügen. Unter Verwendung von FORCED parametrized (im vorherigen Artikel besprochen) wird diese Abfrage parametrisiert.

SELECT * FROM dbo.newsales 
WHERE SalesOrderID < @num;

Wenn wir sicherstellen möchten, dass wir jedes Mal, wenn wir diese Abfrage ausführen, einen neuen Plan erhalten, mit möglicherweise sehr unterschiedlichen Werten für @num, können wir den RECOMPILE-Hinweis wie gezeigt hinzufügen:

SELECT * FROM dbo.newsales
  WHERE SalesOrderID < @num
OPTION (RECOMPILE);

Für gespeicherte Prozeduren haben wir drei Optionen. Zunächst können wir feststellen, ob die Neukompilierung tatsächlich der Performance zugute kommt, indem wir die Prozedur mit der RECOMPILE-Option ausführen:

EXEC get_sales_range 66666 WITH RECOMPILE;

Diese Option bewirkt, dass nur für diese eine Ausführung ein neuer Plan generiert wird. Es wird nicht gespeichert und schon gar nicht wiederverwendet. Der in sp_cacheobjects (im vorherigen Beitrag beschrieben) angezeigte usecount-Wert für die Prozedur wird nicht erhöht, da der ursprüngliche Plan nicht wiederverwendet wird.

Zweitens, wenn wir feststellen, dass die Ausführung von WITH RECOMPILE hilft, könnten wir erwägen, die Prozedur mit der RECOMPILE-Option neu zu erstellen, in diesem Fall wird der Plan nie wiederverwendet und die Prozedur wird überhaupt nicht im Plan-Cache angezeigt.

DROP PROC IF EXISTS get_sales_range;GO
CREATE PROC get_sales_range
   @num int
WITH RECOMPILE
AS
    SELECT * FROM dbo.newsales
    WHERE SalesOrderID < @num;
GO

Für meine einfache kleine Prozedur könnte es sinnvoll sein, die Option WITH RECOMPILE für die gesamte Prozedur zu verwenden. Wenn die Prozedur jedoch komplexer ist, ist es möglicherweise nicht sinnvoll, die gesamte Prozedur neu zu kompilieren, da eine Anweisung Probleme verursacht. Die dritte Option besteht also darin, den RECOMPILE-Hinweis für eine Anweisung innerhalb der Prozedur zu verwenden, also sieht es so aus:

DROP PROC IF EXISTS get_sales_range;
GO
CREATE PROC get_sales_range
   @num int
AS
    SELECT * FROM dbo.newsales
    WHERE SalesOrderID < @num
    OPTION (RECOMPILE);
GO

Die Verwendung einer dieser RECOMPILE-Optionen kann SQL Server dazu zwingen, auf Ihren Wunsch einen neuen Plan zu erstellen. Jetzt sehen wir uns an, wann Ihre SQL Server-Diagnose einen neuen Plan liefert, wenn Sie ihn nicht anfordern, d. h. wann erfolgt die automatische Neukompilierung eines vorhandenen Plans?


Die automatische Neukompilierung eines Plans erfolgt in zwei Arten von Situationen:

  • Wenn der Optimierer zunächst feststellt, dass der vorhandene Plan nicht mehr korrekt ist, normalerweise aufgrund einer Änderung der Objektdefinitionen, muss er einen neuen Plan erstellen. Wenn Sie beispielsweise einen Plan für eine Abfrage haben, die aus TableA auswählt, und Sie dann mehrere Spalten löschen oder Datentypen von Spalten in TableA ändern, kompiliert SQL Server die Abfrage neu, um einen Plan zu erstellen, der die DDL-Änderungen widerspiegelt /li>
  • Die zweite Situation, in der eine automatische Neukompilierung auftritt, ist, wenn SQL Server feststellt, dass der Plan aufgrund einer Änderung der Statistik möglicherweise nicht mehr optimal ist. Wenn seit der letzten Kompilierung des Plans Statistiken zu Spalten oder Indizes aktualisiert wurden, wird er in den meisten Fällen erneut kompiliert. Aber das führt zu einer anderen Frage. Wann werden die Statistiken aktualisiert? Statistiken können automatisch aktualisiert werden, wenn sich genügend Zeilen in den relevanten Spalten geändert haben. Wie viele sind genug? Darüber sprechen wir in Kürze.

Standardmäßig aktualisiert SQL Server die Statistiken automatisch aufgrund einer Datenbankoption, die standardmäßig aktiviert ist. Aber wenn Sie ein Datenbankeigentümer sind (oder ein SQL „sa“, der in jeder Datenbank als Eigentümer erscheint), können Sie die Optionen ändern. Eine der Optionen heißt AUTO_UPDATE_STATISTICS und eine andere heißt AUTO_UPDATE_STATISTICS_ASYNC. Die Option AUTO_UPDATE_STATISTICS ist in der tempdb-Datenbank aktiviert, sodass jede neue Datenbank diese Option erbt. Wenn diese Option aktiviert ist und das Abfrageausführungsmodul Änderungen an einer ausreichenden Anzahl von Zeilen erkennt, während eine Abfrage verarbeitet wird, wird die Ausführung angehalten, während die Statistiken aktualisiert werden, und dann wird die Abfrage neu kompiliert. Die andere Option, AUTO_UPDATE_STATISTICS_ASYNC, kann sich möglicherweise weniger auf die Ausführungszeit der Abfrage auswirken, da die Ausführung nicht angehalten wird, was auf Kosten der Verwendung eines möglicherweise suboptimalen Plans geht. Bei der zweiten Option wird, wenn das Ausführungsmodul erkennt, dass Statistiken aktualisiert werden müssen, ein Hintergrund-Thread gestartet, um die Aktualisierung durchzuführen, und der Haupt-Thread fährt mit der Ausführung der Abfrage mit den ursprünglichen Statistiken und dem ursprünglichen Plan fort. Die nächste Abfrage, die auf die betroffenen Tabellen zugreift und die aktualisierten Statistiken sieht, wird die Abfrage neu kompilieren, aber sie wird nicht anhalten und die Statistiken nicht mitten in der Ausführung aktualisieren.

Es gibt ein paar weitere Situationen sowie einige Abfragehinweise, die steuern, ob Pläne neu kompiliert werden oder nicht, also zeige ich Ihnen ein Flussdiagramm. Ich teile ein Flussdiagramm mit Ihnen, das ich für meine Schulungskurse zu SQL Server-Interna erstellt habe /P>

Der Pfeil ist der Punkt, an dem SQL Server mit der Verarbeitung Ihres Stapels beginnt. Es prüft zunächst, ob bereits ein Plan für Ihren Stapel im Cache vorhanden ist, und wenn die Antwort NEIN lautet, folgt es dem Pfeil nach rechts und erstellt einen Plan. Der Plan wird in den Cache gestellt, und SQL Server startet erneut. Ja, der Plan sollte diesmal im Cache sein, also folgt er dem Pfeil nach unten und fragt, ob ein Hinweis namens KEEP PLAN verwendet wurde. Wenn JA, beginnt SQL Server sofort mit der Ausführung des Plans und führt keine weiteren Überprüfungen durch.

Die nächste Frage ist, ob irgendwelche DDL-Änderungen vorgenommen wurden. Wenn nein, fragt es nach mehreren anderen Situationen, über die ich in diesem Artikel nicht sprechen kann. Tatsächlich werde ich hier wirklich nicht jede Option durchgehen. Das überlasse ich dir. Aber wenn Sie Fragen oder Unklarheiten haben, können Sie diese gerne im Kommentarbereich hier stellen oder mir unter @sqlqueen twittern. Ich möchte auf die Frage ganz rechts hinweisen:Ist AUTO_STATS_ASYNC EIN? Hier können Sie sehen, dass es zwei Aktionen gibt, wenn die Antwort JA ist. Ein Zweig beginnt einfach mit der Ausführung mit dem vorhandenen Plan, und der andere ist der Hintergrund-Thread, der die Statistiken aktualisiert, aber dann nichts weiter tut. Die nächste Abfrage trifft auf das Entscheidungsfeld in der Mitte „Sind neue Statistiken verfügbar“ und sollte mit JA antworten, sodass die Abfrage neu kompiliert wird.

Die einzige andere Sache, über die ich sprechen werde, ist die Frage „Sind irgendwelche Statistiken veraltet?“ Dies bedeutet im Grunde, dass die Statistiken veraltet sind, weil zu viele Änderungen vorgenommen wurden. Jetzt können wir also darüber sprechen, wie viele zu viele sind.

Obwohl für sehr kleine Tabellen unterschiedliche Werte verwendet werden, wurden Statistiken für jede Tabelle mit mehr als 500 Zeilen darin vor SQL Server 2016 als „veraltet“ betrachtet, wenn die Anzahl der Änderungen an der Spalte, auf der die Statistiken basierten, 20 überstieg % der Anzahl der Zeilen in der Tabelle. Für eine Tabelle mit 1000 Zeilen könnte dies also 200 Einfügungen, 200 Aktualisierungen oder 200 Löschungen bedeuten. Es können 200 Zeilenänderungen oder 5 Zeilen sein, die jeweils 40 Mal aktualisiert werden. SQL Server gibt uns sogar eine Funktion, die meldet, wie viele Änderungen vorgenommen wurden. Sie müssen die stats_id-Nummer für die Statistiken nachschlagen, an denen Sie interessiert sind. Dies wäre die index_id, wenn die Statistiken zu einem Index gehören. Die stats_id finden Sie in der Ansicht namens sys.stats. In meiner Newssales-Tabelle verwende ich diese Abfrage, um herauszufinden, dass die stats_id für den Index der Spalte SubTotal 3 ist.

SELECT name, stats_id FROM sys.stats
WHERE object_id = object_id('newsales');

Dann kann ich diesen Wert verwenden, um mir die Anzahl der Änderungen anzusehen. Lassen Sie mich zuerst einige Zeilen aktualisieren:

UPDATE newsales
SET SubTotal = SubTotal * 0.9
WHERE SalesOrderID < 45200
(1541 Zeilen betroffen)

SELECT * FROM sys.dm_db_stats_properties(object_id('newsales'), 3);  

Tatsächlich sind 20 % eine GROSSE Zahl. Und für viele Tabellen könnten Abfragen von aktualisierten Statistiken profitieren, wenn weit weniger als 20 % der Zeilen aktualisiert werden. Ab 2008R2 SP1 enthielt SQL Server ein Traceflag, mit dem Sie die Anzahl der Zeilen in eine gleitende Skala ändern konnten, wie im folgenden Diagramm gezeigt:

Ab SQL Server 2016 wird dieser neue Algorithmus mit der gleitenden Skala standardmäßig verwendet, solange Sie sich im Kompatibilitätsgrad 130 oder höher befinden.

Die meisten automatischen Neukompilierungen von Abfrageplänen sind auf Änderungen in Statistiken zurückzuführen. Aber wie ich oben erwähnt habe, ist das nicht der einzige Grund für eine Neukompilierung. Da dies jedoch am häufigsten vorkommt, kann es sehr nützlich sein, zu wissen, wann und wie Statistiken aktualisiert werden, und sicherzustellen, dass die Statistiken in Ihren kritischen Tabellen oft genug aktualisiert werden, um sicherzustellen, dass Sie die besten Pläne erhalten!

Analysieren Sie Leistungsdaten automatisch, um eine SQL-Server-Diagnose durchzuführen, um Probleme schnell zu lösen und Server zu identifizieren, von denen Leistungseinbußen stammen. Beginnen Sie noch heute mit Spotlight Cloud: