Plan-Regressionen

Eine Abfrage, die gestern 40 ms brauchte und heute 400 ms, hat sich meist nicht selbst geändert — sie hat einen anderen Ausführungsplan bekommen. Query Store hält diese Historie auf der überwachten Instanz vor: pro Abfrage, pro Plan, mit Laufzeiten.

Das ist der Unterschied zum Plan-Cache, aus dem der Rest der Anwendung liest. Der Cache vergisst: bei jedem Neustart, unter Speicherdruck, bei jeder Neukompilierung. Query Store nicht — und er behält den alten Plan, sodass die Frage „welcher Plan war es vorher" überhaupt beantwortbar wird.

Was die Seite zeigt

Unter Performance → Query Store steht je Zeile eine Abfrage, deren aktueller Plan messbar langsamer ist als ein anderer Plan derselben Abfrage, den wir im selben Zeitfenster beobachtet haben.

Sortiert wird nach Mehraufwand pro Stunde, nicht nach dem Faktor. Eine Abfrage, die von 30 ms auf 90 ms geht und dreimal am Tag läuft, ist Arithmetik; eine, die von 200 ms auf 500 ms geht und zehntausendmal pro Stunde läuft, ist der Grund, warum der Server langsam ist. Der Faktor steht daneben, er entscheidet aber nicht die Reihenfolge.

Warum manche Abfragen nicht auftauchen

Vier Schwellen halten die Liste lesbar. Sie sind absichtlich so gesetzt, dass eher etwas fehlt als dass die Liste unbrauchbar wird:

  • Mindestens 10 Ausführungen je Plan. Zwei Ausführungen eines kalten Plans messen den Cache, nicht den Plan.
  • Mindestens 20 ms auf dem aktuellen Plan. Von 0,4 ms auf 4 ms ist eine Verzehnfachung und trotzdem bedeutungslos.
  • Mindestens Faktor 2.
  • Mindestens 1 Sekunde Mehraufwand pro Stunde.

Ein Vergleichsplan wird außerdem nur vorgeschlagen, wenn er selbst genug Ausführungen hat. Einen Plan aufgrund von drei Ausführungen zu erzwingen ist der Weg, wie ein Überwachungswerkzeug die Lage verschlechtert.

Abdeckung lesen

Über der Tabelle steht, wie viele Datenbanken gelesen und wie viele übersprungen wurden. Das ist kein Beiwerk: In den meisten Umgebungen ist Query Store nicht überall aktiviert, und eine leere Tabelle bedeutet dann nicht „alles in Ordnung", sondern „hier wurde nicht nachgesehen". Die Abzeichen nennen den Grund — ausgeschaltet, schreibgeschützt, noch keine Daten, keine Berechtigung, zu alte SQL-Server-Version.

Schreibgeschützt ist der Fall, den man nicht übersehen sollte: Query Store ist eingeschaltet, hat aber sein Speicherlimit erreicht und zeichnet nichts Neues mehr auf. Der Detailtext am Abzeichen nennt die Ursache, die SQL Server selbst meldet.

Query Store einschalten

Pro Datenbank, einmalig:

ALTER DATABASE [MeineDatenbank] SET QUERY_STORE = ON;
ALTER DATABASE [MeineDatenbank] SET QUERY_STORE (OPERATION_MODE = READ_WRITE);

Ab SQL Server 2016 verfügbar, ab SQL Server 2022 in neuen Datenbanken standardmäßig an. In Azure SQL-Datenbank ist er ohnehin aktiv.

Einen Plan erzwingen

Ausgewählte Zeilen erzeugen ein Skript mit sp_query_store_force_plan — und ein passendes Gegenstück zum Zurücknehmen.

Die Anwendung führt das nie selbst aus. Ein erzwungener Plan übergeht den Optimizer dauerhaft, übersteht Neustarts und ist genau die Art Entscheidung, die einen Namen tragen sollte. Das Skript ist zum Lesen, Anpassen und Ausführen im eigenen Wartungsfenster gedacht — dieselbe Regel wie bei den Index-Aktionen.

Wo SQL Server einen Plan schon einmal nicht erzwingen konnte, wird die Zeile weiterhin angezeigt, der Vorschlag aber zurückgehalten: Was einmal nicht angewendet werden konnte, wird es durch einen zweiten Versuch nicht.

Alarme

Wo Query Store einen Server abdeckt, ist er auch die Quelle für den Alarm „System: Plan Regression Detected" — die ältere Auswertung aus dem Plan-Cache tritt für diesen Server zurück. Es gibt also nur eine Regel, einen Cooldown und eine Benachrichtigung; nur der Befund kommt aus der besseren Quelle. Für Server ohne Query Store bleibt der Plan-Cache die Grundlage.

Alarmiert wird deutlich zurückhaltender als aufgelistet: erst ab 10 Sekunden Mehraufwand pro Stunde und höchstens fünf Meldungen je Durchlauf. Eine Statistik-Aktualisierung kann dutzende Pläne auf einmal kippen — ohne Deckel wäre das ein Postfach voll einzeln berechtigter Alarme, und genau so bekommt ein Alarmkanal eine Filterregel verpasst. Wie viele Befunde der Deckel zurückgehalten hat, steht in der Meldung.

Dieselbe Abfrage auf demselben Planpaar meldet sich innerhalb des Sammelfensters nur einmal. Wechselt sie auf einen dritten Plan, ist das ein neuer Befund.

Sammlung

Ein Hintergrunddienst liest standardmäßig stündlich, mit einem Zeitfenster von 24 Stunden. Anders als bei der Änderungsverfolgung ist das kein Aufbewahrungsschalter: Query Store führt seine Historie selbst, ein längeres Intervall bedeutet nur seltener nachsehen. Über Jetzt sammeln lässt sich ein Durchlauf von Hand auslösen (Administratorrolle, weil er Verbindungen zu jeder Datenbank der Instanz öffnet).