it-swarm.com.de

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

In den letzten 5 Wochen beginnt SQL Server 2016 (AWS RDS, gespiegelt) in den letzten 5 Wochen ungefähr zur gleichen Tageszeit (am frühen Morgen kann dies auf Benutzeraktivitäten beruhen, wenn Benutzer damit beginnen) eine Zeitüberschreitung Anfragen.

UPDATE STATISTICS für alle Tabellen behebt das Problem immer sofort.

Nach dem ersten Mal habe ich dafür gesorgt, dass alle Statistiken auf allen Tabellen jede Nacht (statt wöchentlich) aktualisiert werden, aber es ist immer noch passiert (ungefähr 8 Stunden, nachdem die Aktualisierungsstatistik ausgeführt wurde, aber nicht jeden Tag, an dem sie ausgeführt wird).

Beim letzten Mal habe ich den Abfragespeicher aktiviert, um festzustellen, ob es sich um eine bestimmte Abfrage/einen bestimmten Abfrageplan handelt. Ich glaube, ich konnte es auf eins eingrenzen:

(Bad query plan

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 Erstellen des neuen Index nur. Aber selbst ohne diesen Index kehrte er in 99% der Fälle innerhalb weniger Millisekunden zurück, aber dann würde es wöchentlich> 40 Sekunden dauern.

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

DBCC CHECKDB gibt keine Fehler zurück.

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

Indizes, die ich gerade hinzugefügt habe:

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/Z5szPBf (LINQ-generiert, ich kann/sollte in der Lage sein, ausgewählte Spalten zu optimieren, aber es sollte für dieses Problem irrelevant sein)

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 neuer 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 kann es bei einigen Abfragen mit dem Legacy-CE zu einer besseren Leistung kommen. Daher passen einige Teile Ihres Datenmodells möglicherweise nicht am besten zum neuen CE. 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 Statistikaktualisierungen. Eine wichtige Sache, die Sie beachten sollten, ist, dass das Sammeln von Statistiken für alle Tabellen effektiv alle Abfragepläne aus dem Cache löscht, sodass Sie möglicherweise ein Problem mit Statistiken haben oder mit dem Parameter-Sniffing zu tun haben. Es ist schwierig, eine Entscheidung zu treffen, ohne viele Informationen über Ihr Datenmodell, die Datenänderungsrate, Richtlinien zur Aktualisierung von Statistiken, das Aufrufen Ihres Codes usw. zu haben. SQL Server 2016 bietet einige Einstellungen auf Datenbankebene für das Parameter-Sniffing) was hilfreich sein könnte, aber das könnte Ihre gesamte Anwendung betreffen, anstatt nur die eine problematische Abfrage.

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

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

Angenommen, Sie sammeln 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 funktioniert, für Benutzer mit 20.000 Datensätzen jedoch schrecklich, wenn die erste Abfrage des Tages gegen einen Benutzer mit nur 1 Berechtigungsdatensatz gerichtet 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, endet jedoch möglicherweise in ms. Es klingt wirklich nach Parameter-Sniffing. Es erklärt, warum das Problem nicht immer angezeigt wird oder warum es manchmal Stunden dauert, bis es angezeigt wird.

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

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

(bad query plan

SQL Server schätzt, dass nur eine Zeile vom Join für [Permission] Und [Project] Zurückgegeben wird. Für jede Zeile in der äußeren Eingabe wird ein Clustered-Index-Scan für [Appointment] Durchgeführt. Alle Zeilen werden aus dieser Tabelle gescannt, aber nur diejenigen, die der Filterung nach [Start] Entsprechen, werden an den Join-Operator 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 aus dem Join falsch ist und wir beispielsweise 1000 Zeilen erhalten, führt SQL Server 1000 Clustered-Index-Scans für [Appointment] Durch. Die Leistung des Abfrageplans ist sehr empfindlich gegenüber Schätzungsproblemen.

Der direkteste Weg, um diesen Abfrageplan nie wieder zu erhalten, besteht darin, einen Deckungsindex für die Tabelle [Appointment] Zu erstellen. So etwas wie ein Index für [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 auf [Permission] Und [Project] Zu korrigieren. Typische Möglichkeiten hierfür sind das Ändern des Codes, das Aktualisieren von Statistiken, die Verwendung des Legacy-CE, das Erstellen mehrspaltiger Statistiken, das Bereitstellen weiterer Informationen zu lokalen Variablen durch SQL Server, z. B. mit einem RECOMPILE - Hinweis oder das Materialisieren dieser Zeilen in a temporäre Tabelle. Viele dieser Techniken sind kein guter Ansatz, wenn Sie eine Antwortzeit auf MS-Ebene benötigen oder Code über ein ORM schreiben müssen.

Der Index, den Sie für [AppointmentAttendee] Erstellt haben, ist kein direkter Weg, um das Problem zu beheben. Sie erhalten jedoch mehrspaltige Statistiken zum Index, und diese Statistiken können den fehlerhaften Abfrageplan entmutigen. Der Index bietet möglicherweise eine effizientere Möglichkeit, auf die Daten zuzugreifen, was möglicherweise auch den Plan für fehlerhafte Abfragen entmutigt, aber ich glaube nicht, dass es irgendeine Garantie dafür gibt, dass dies nicht nur mit dem Index auf [AppointmentAttendee] Wieder vorkommt. .

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

Ich verstehe, warum Sie diese Frage stellen, aber sie ist äußerst weit gefasst. Mein einziger Rat ist, zu versuchen, die Hauptursache für die Instabilität des Abfrageplans besser zu verstehen, zu überprüfen, ob Sie die richtigen Indizes für Ihre Arbeitslast erstellt haben, und Ihre Arbeitslast sorgfältig zu testen und zu überwachen. Microsoft hat einige allgemeine Ratschläge zum Umgang mit Abfrageplan-Regressionen, 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 sie auf der vorherigen Stufe 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 ausreichend, 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 nach der Änderung der Kompatibilitätsstufe Leistungsabweichungen bei bestimmten Abfragen auftreten

  6. Erzwingen Sie in Fällen mit Regressionen den vorherigen Plan im Abfragespeicher.

  7. Wenn es Abfragepläne gibt, die nicht erzwungen werden können oder die Leistung immer noch nicht ausreicht, sollten Sie die Kompatibilitätsstufe auf die vorherige Einstellung zurücksetzen und dann den Microsoft-Kundensupport aktivieren.

Ich sage nicht, dass Sie ein Downgrade auf SQL Server 2012 durchführen und neu 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 geeignet ist, mit den Funktionen des Abfragespeichers vertraut ist und die Sicherheit haben möchten, die mit dem Erzwingen eines Abfrageplans verbunden ist, sollten Sie sich dafür entscheiden. Das Erzwingen von Abfrageplänen mit Regressionen ist schließlich Teil der von Microsoft empfohlenen Upgrade-Richtlinie für SQL Server 2016.

16
Joe Obbish