SQL Server 2016 Bad Query Plan sperrt die Datenbank einmal pro Woche


16

In den letzten 5 Wochen tritt bei SQL Server 2016 (AWS RDS, gespiegelt) in etwa zur gleichen Tageszeit (am frühen Morgen, möglicherweise abhängig von der Benutzeraktivität, wenn Benutzer mit der Verwendung beginnen) eine Zeitüberschreitung auf Abfragen.

UPDATE STATISTICS für alle Tabellen behebt es immer sofort.

Nach dem ersten Mal habe ich jede Nacht (statt wöchentlich) alle Statistiken auf allen Tabellen aktualisiert, aber es ist trotzdem passiert (ca. 8 Stunden nach Ausführung der Update-Statistiken, aber nicht an jedem Tag, an dem sie ausgeführt werden).

Beim letzten Mal habe ich den Abfragespeicher aktiviert, um festzustellen, welche bestimmte Abfrage bzw. welcher Abfrageplan vorhanden ist. Ich glaube, ich konnte es auf eins eingrenzen:

Schlechter Abfrageplan

Nachdem ich diese Abfrage gefunden hatte, fügte ich einen empfohlenen Index hinzu, der in dieser nicht häufig verwendeten Abfrage fehlte (der jedoch viele häufig verwendete Tabellen berührt).

Der fehlerhafte Abfrageplan führte einen Index-Scan durch (für eine Tabelle mit nur 10.000 Zeilen). Andere Abfragepläne, die in Millisekunden zurückgegeben wurden, führten jedoch denselben Scan durch. Der neueste Abfrageplan sucht nach dem Anlegen des neuen Index nur. Aber selbst ohne diesen Index kehrte er in 99% der Fälle innerhalb weniger Millisekunden zurück, und dann dauerte er wöchentlich> 40 Sekunden.

Dies begann nach dem Wechsel zu SQL Server 2016 von 2012.

DBCC CHECKDB gibt keine Fehler zurück.

  1. Behebt der neue Index das Problem, sodass er den schlechten Plan nie wieder auswählt?
  2. Soll ich den Plan "erzwingen", der jetzt gut funktioniert?
  3. Wie stelle ich sicher, dass dies nicht bei einer anderen Abfrage / einem anderen Plan passiert?
  4. Ist dies ein Symptom für ein größeres Problem?

Von mir soeben hinzugefügte Indizes:

CREATE NONCLUSTERED INDEX idx_AppointmetnAttendee_AttendeeType
ON [dbo].[AppointmentAttendee] ([UserID],[AttendeeType])

CREATE NONCLUSTERED INDEX [idx_appointment_start] ON [dbo].[Appointment]
(
    [ProjectID] ASC,
    [Start] ASC
)
INCLUDE (   [ID],
    [AllDay],
    [End],
    [Location],
    [Notes],
    [Title],
    [CreatedByID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

Vollständiger Abfragetext:

https://pastebin.com/Z5szPBfu (LINQ-generiert, ich kann / sollte in der Lage sein, ausgewählte Spalten zu optimieren, dies sollte jedoch für dieses Problem irrelevant sein.)


Mir ist gerade aufgefallen, dass sich der Scan der vorherigen Pläne, bei dem keine Zeitüberschreitung auftrat, auf einem anderen Tisch in der gleichen Größe befand. Termin: 11931 Zeilen, Terminempfänger: 11937 Zeilen.
Professional Sounding Name

Antworten:


16

Ich werde Ihre Fragen in einer anderen Reihenfolge beantworten, als Sie sie gestellt haben.

4. Ist dies ein Symptom für ein größeres Problem?

Der neue Kardinalitätsschätzer in SQL Server 2016 könnte zu dem Problem beitragen. SQL Server 2012 verwendet das ältere CE, und bei dieser Version ist Ihr Problem nicht aufgetreten. Der neue Kardinalitätsschätzer nimmt unterschiedliche Annahmen zu Ihren Daten vor und kann unterschiedliche Abfragepläne für dasselbe SQL generieren. Abhängig von Ihrer Abfrage und Ihren Daten können Sie bei einigen Abfragen mit dem Legacy-CE eine bessere Leistung erzielen. Einige Teile Ihres Datenmodells stimmen möglicherweise nicht mit dem neuen CE überein. Das ist in Ordnung, aber möglicherweise müssen Sie das neue CE erst einmal umgehen.

Ich würde mich auch mit inkonsistenter Abfrageleistung befassen, selbst bei täglichen Statistiken-Updates. Es ist wichtig zu beachten, dass beim Sammeln von Statistiken für alle Tabellen alle Abfragepläne aus dem Cache gelöscht werden, sodass möglicherweise ein Problem mit der Statistik oder mit dem Parameter-Sniffing auftritt. Es ist schwierig, eine Entscheidung zu treffen, ohne viele Informationen zu Ihrem Datenmodell, der Datenänderungsrate, den Richtlinien zur Aktualisierung der Statistiken, dem Aufruf des Codes usw. zu haben. SQL Server 2016 bietet einige Einstellungen auf Datenbankebene für das Parameter-Sniffing, die hilfreich sein könnten Dies könnte sich jedoch auf Ihre gesamte Anwendung auswirken, anstatt nur auf eine problematische Abfrage.

Ich werde ein Beispielszenario wegwerfen, das zu diesem Verhalten führen könnte. Du sagtest:

Einige Benutzer können 1 Berechtigungsdatensatz haben, andere bis zu 20.000.

Angenommen, Sie erfassen Statistiken für alle Tabellen, wodurch alle Abfragepläne gelöscht werden. Abhängig von den oben genannten Faktoren kann SQL Server einen Plan zwischenspeichern, der für Benutzer mit 1 Datensatz gut geeignet ist, für Benutzer mit 20.000 Datensätzen jedoch fürchterlich funktioniert, wenn die erste Abfrage des Tages für einen Benutzer mit nur 1 Berechtigungsdatensatz bestimmt ist. Wenn die erste Abfrage des Tages gegen einen Benutzer mit 20.000 Datensätzen gerichtet ist, erhalten Sie möglicherweise einen guten Plan für 20.000 Datensätze. Wenn der Code für einen Benutzer mit 1 Datensatz ausgeführt wird, ist er möglicherweise nicht die optimalste Abfrage, wird jedoch möglicherweise in ms beendet. Es klingt wirklich nach Parameter-Sniffing. Es erklärt, warum Sie das Problem nicht immer sehen oder warum es manchmal Stunden dauert, bis es angezeigt wird.

1. Behebt der neue Index das Problem, sodass er den schlechten Plan nie wieder auswählt?

Ich denke, dass einer der von Ihnen hinzugefügten Indizes das Problem verhindern wird, da der Zugriff auf die erforderlichen Daten über den Index günstiger ist als ein Clustered-Index-Scan für die Tabelle, insbesondere wenn der Scan nicht vorzeitig beendet werden kann. Vergrößern wir den fehlerhaften Teil des Abfrageplans:

schlechter Abfrageplan

SQL Server schätzt, dass vom Join on [Permission]und nur eine Zeile zurückgegeben wird [Project]. Für jede Zeile in der äußeren Eingabe wird ein Clustered-Index-Scan durchgeführt [Appointment]. Alle Zeilen werden aus dieser Tabelle gescannt, aber nur diejenigen, die der Filterung entsprechen [Start], werden an den Verknüpfungsoperator zurückgegeben. Innerhalb des Join-Operators werden die Ergebnisse weiter reduziert.

Der oben beschriebene Abfrageplan kann in Ordnung sein, wenn wirklich nur eine Zeile an die äußere Eingabe des Joins gesendet wird. Wenn jedoch die Kardinalitätsschätzung des Joins falsch ist und wir beispielsweise 1000 Zeilen erhalten, führt SQL Server 1000 Clustered-Index-Scans durch [Appointment]. Die Leistung des Abfrageplans reagiert sehr empfindlich auf Schätzungsprobleme.

Die direkteste Möglichkeit, diesen Abfrageplan nie wieder abzurufen, besteht darin, einen abdeckenden Index für die [Appointment]Tabelle zu erstellen . So etwas wie ein Index auf [ProjectId]und [Start]sollte es tun. Es sieht so aus, als wäre dies genau der [idx_appointment_start]Index, den Sie erstellt haben, um das Problem zu beheben. Eine andere Möglichkeit, SQL Server von der Auswahl des Abfrageplans abzuhalten, besteht darin, die Kardinalitätsschätzung aus dem Join on [Permission]und zu korrigieren [Project]. Typische Methoden hierfür sind das Ändern des Codes, das Aktualisieren von Statistiken, die Verwendung des Legacy-CE, das Erstellen von mehrspaltigen Statistiken, das Bereitstellen weiterer Informationen zu lokalen Variablen in SQL Server, z. B. mit einem RECOMPILEHinweis, oder das Materialisieren dieser Zeilen in einer temporären Tabelle. Viele dieser Techniken sind kein guter Ansatz, wenn Sie Antwortzeiten auf MS-Ebene benötigen oder Code über einen ORM schreiben müssen.

Der Index, den Sie erstellt haben, [AppointmentAttendee]ist keine direkte Möglichkeit, das Problem zu beheben. Sie erhalten jedoch mehrspaltige Statistiken zum Index, die den fehlerhaften Abfrageplan möglicherweise entmutigen. Der Index bietet möglicherweise eine effizientere Möglichkeit, auf die Daten zuzugreifen, was auch den schlechten Abfrageplan entmutigt, aber ich glaube nicht, dass es irgendeine Garantie dafür gibt, dass dies nicht noch einmal vorkommt, nur wenn der Index aktiviert ist [AppointmentAttendee].

3. Wie stelle ich sicher, dass dies nicht bei einer anderen Abfrage / einem anderen Plan passiert?

Ich verstehe, warum Sie diese Frage stellen, aber sie ist extrem weit gefasst. Mein einziger Rat ist, die Ursache der Instabilität des Abfrageplans besser zu verstehen, zu überprüfen, ob die richtigen Indizes für Ihre Arbeitslast erstellt wurden, und Ihre Arbeitslast sorgfältig zu testen und zu überwachen. Microsoft hat einige allgemeine Ratschläge zum Umgang mit Regressionen von Abfrageplänen, die durch das neue CE in SQL Server 2016 verursacht werden:

Der empfohlene Workflow zum Aktualisieren des Abfrageprozessors auf die neueste Version des Codes lautet:

  1. Aktualisieren Sie eine Datenbank auf SQL Server 2016, ohne die Datenbankkompatibilitätsstufe zu ändern (behalten Sie die vorherige Version bei).

  2. Aktivieren Sie den Abfragespeicher in der Datenbank. Weitere Informationen zum Aktivieren und Verwenden des Abfragespeichers finden Sie unter Überwachen der Leistung mithilfe des Abfragespeichers.

  3. Warten Sie genügend Zeit, um repräsentative Daten der Arbeitslast zu erfassen.

  4. Ändern Sie die Kompatibilitätsstufe der Datenbank auf 130

  5. Überprüfen Sie mit SQL Server Management Studio, ob bei bestimmten Abfragen nach der Änderung der Kompatibilitätsstufe Leistungseinbußen auftreten

  6. Erzwingen Sie in Fällen, in denen es zu Regressionen kommt, den vorherigen Plan im Abfragespeicher.

  7. Wenn Abfragepläne nicht erzwungen werden oder die Leistung immer noch unzureichend ist, können Sie die Kompatibilitätsstufe auf die vorherige Einstellung zurücksetzen und anschließend den Microsoft-Kundensupport einschalten.

Ich sage nicht, dass Sie ein Downgrade auf SQL Server 2012 durchführen und von vorne beginnen müssen, aber die beschriebene allgemeine Technik kann für Sie nützlich sein.

2. Soll ich den Plan "erzwingen", der jetzt gut funktioniert?

Es liegt ganz bei Ihnen. Wenn Sie der Meinung sind, dass Sie einen Abfrageplan haben, der für alle möglichen Eingabeparameter gut funktioniert, mit der Funktionalität des Abfragespeichers vertraut ist und die Sicherheit haben möchten, die mit dem Erzwingen eines Abfrageplans einhergeht, dann entscheiden Sie sich. Das Erzwingen von Abfrageplänen mit Regressionen ist schließlich Teil der von Microsoft empfohlenen Upgrade-Richtlinie für SQL Server 2016.

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.