Lang laufende Abfrage auf schreibgeschütztem Replikat, die auf der Primärdatenbank einige Zeit in Anspruch nimmt

8

Ich habe ein 4-Knoten-AG-Setup wie folgt:

VM-Hardwarekonfiguration aller Knoten:

  • Microsoft SQL Server 2017 Enterprise Edition (RTM-CU14) (KB4484710)
  • 16 vCPUs
  • 356 GB RAM (lange Geschichte zu diesem ...)
  • Maximaler Parallelitätsgrad: 1 (wie vom App-Anbieter gefordert)
  • Kostenschwelle für Parallelität: 50
  • Maximaler Serverspeicher (MB): 338944 (331 GB)

AG Konfiguration:

  • Knoten 1: Primäre oder synchrone Festschreibung Nicht lesbar Sekundär, konfiguriert für automatisches Failover
  • Knoten 2: Primäre oder synchrone Festschreibung Nicht lesbar Sekundär, konfiguriert für automatisches Failover
  • Knoten 3: Lesbarer Sekundärsatz mit asynchronem Commit, konfiguriert für manuelles Failover
  • Knoten 4: Lesbarer Sekundärsatz mit asynchronem Commit, konfiguriert für manuelles Failover

Die fragliche Frage:

Diese Abfrage ist nicht besonders verrückt. Sie enthält eine Zusammenfassung der ausstehenden Arbeitselemente in verschiedenen Warteschlangen innerhalb der Anwendung. Sie können den Code über einen der folgenden Ausführungsplan-Links sehen.

Ausführungsverhalten auf dem Primärknoten:

Bei der Ausführung auf dem Primärknoten liegt die Ausführungszeit im Allgemeinen um die 1-Sekunden-Marke. Hier ist der Ausführungsplan. Nachfolgend sind die Statistiken aufgeführt, die von STATISTICS IO und STATISTICS TIME vom Primärknoten erfasst wurden:

(347 rows affected)
Table 'Worktable'. Scan count 647, logical reads 2491, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'workitemlc'. Scan count 300, logical reads 7125, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Workfile'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulertask'. Scan count 1, logical reads 29, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'wfschedulertask'. Scan count 1, logical reads 9, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulerservice'. Scan count 1, logical reads 12, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulerworkerpool'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'itemlc'. Scan count 1, logical reads 26372, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row affected)

 SQL Server Execution Times:
   CPU time = 500 ms,  elapsed time = 656 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

Ausführungsverhalten auf dem schreibgeschützten Sekundärknoten:

Bei der Ausführung auf einem schreibgeschützten sekundären Knoten (dh Knoten 3 oder Knoten 4) verwendet diese Abfrage denselben Ausführungsplan (dies ist eine andere Planverknüpfung), und es werden ungefähr dieselben Ausführungsstatistiken angezeigt (z. B. kann es einige weitere Seiten geben Scans, da sich diese Ergebnisse ständig ändern), aber mit Ausnahme der CPU-Zeit sehen sie sehr ähnlich aus. Hier sind Statistiken, die aus STATISTICS IO und STATISTICS TIME vom schreibgeschützten Sekundärknoten erfasst wurden:

(347 rows affected)
Table 'Worktable'. Scan count 647, logical reads 2491, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'workitemlc'. Scan count 300, logical reads 7125, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Workfile'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulertask'. Scan count 1, logical reads 29, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'wfschedulertask'. Scan count 1, logical reads 9, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulerservice'. Scan count 1, logical reads 12, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulerworkerpool'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'itemlc'. Scan count 1, logical reads 26372, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row affected)

 SQL Server Execution Times:
   CPU time = 55719 ms,  elapsed time = 56335 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

Andere Details:

Ich habe auch beide sp_WhoIsActiveund Paul RandalsWaitingTasks.sql Skript auf der Sekundärseite ausgeführt, während diese Abfrage ausgeführt wird, aber ich sehe keine Wartezeiten, was ehrlich gesagt frustrierend ist:

Geben Sie hier die Bildbeschreibung ein

Dies scheint auch kein Fall von AG-Latenz zu sein, da der Synchronisationsstatus tatsächlich recht gut ist:

--https://sqlperformance.com/2015/08/monitoring/availability-group-replica-sync

SELECT 
       ar.replica_server_name, 
       adc.database_name, 
       ag.name AS ag_name, 
       drs.is_local, 
       drs.synchronization_state_desc, 
       drs.synchronization_health_desc, 
       --drs.last_hardened_lsn, 
       --drs.last_hardened_time, 
       drs.last_redone_time, 
       drs.redo_queue_size, 
       drs.redo_rate, 
       (drs.redo_queue_size / drs.redo_rate) / 60.0 AS est_redo_completion_time_min,
       drs.last_commit_lsn, 
       drs.last_commit_time
FROM sys.dm_hadr_database_replica_states AS drs
INNER JOIN sys.availability_databases_cluster AS adc 
       ON drs.group_id = adc.group_id AND 
       drs.group_database_id = adc.group_database_id
INNER JOIN sys.availability_groups AS ag
       ON ag.group_id = drs.group_id
INNER JOIN sys.availability_replicas AS ar 
       ON drs.group_id = ar.group_id AND 
       drs.replica_id = ar.replica_id
ORDER BY 
       ag.name, 
       ar.replica_server_name, 
       adc.database_name;

Geben Sie hier die Bildbeschreibung ein

Diese Abfrage scheint der schlimmste Täter zu sein. Andere Abfragen, die auf dem Primärknoten ebenfalls weniger als eine Sekunde dauern, können auf dem Sekundärknoten 1 bis 5 Sekunden dauern, und obwohl das Verhalten nicht so schwerwiegend ist, scheint es Probleme zu verursachen.

Schließlich habe ich mir auch die Server angesehen und nach externen Prozessen wie A / V-Scans, externen Jobs, die unerwartete E / A erzeugen, usw. gesucht und bin mit leeren Händen aufgetaucht. Ich glaube nicht, dass dies durch irgendetwas außerhalb des SQL Server-Prozesses verursacht wird.

Die Frage:

Es ist erst Mittag, wo ich bin, und es war schon ein langer Tag, also vermute ich, dass mir hier etwas Offensichtliches fehlt. Entweder das, oder wir haben etwas falsch konfiguriert, was möglich ist, da wir eine Reihe von Anrufen beim Anbieter und in der MS im Zusammenhang mit dieser Umgebung erhalten haben.

Bei all meinen Nachforschungen kann ich einfach nicht herausfinden, was diesen Leistungsunterschied verursacht. Ich würde erwarten, dass auf den sekundären Knoten eine Art Wartezeit auftritt, aber nichts. Wie kann ich dieses Problem weiter beheben, um die Grundursache zu ermitteln? Hat jemand dieses Verhalten schon einmal gesehen und einen Weg gefunden, es zu beheben?

UPDATE Nr. 1 Nachdem die Status des dritten Knotens (eines der schreibgeschützten Replikate) in nicht lesbare und dann als Test wieder in lesbar getauscht wurden, wird dieses Replikat weiterhin von einer offenen Transaktion aufgehalten, wobei alle Clientabfragen das anzeigen HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONINGwarten.

Das Ausführen eines DBCC OPENTRANBefehls führt zu folgenden Ergebnissen:

Oldest active transaction:
    SPID (server process ID): 420s
    UID (user ID) : -1
    Name          : QDS nested transaction
    LSN           : (941189:33148:8)
    Start time    : May  7 2019 12:54:06:753PM
    SID           : 0x0
DBCC execution completed. If DBCC printed error messages, contact your system administrator.

Wenn Sie diese SPID nachschlagen sp_who2, wird sie als BACKGROUNDProzess angezeigt , der QUERY STORE BACKals Befehl aufgeführt ist.

Obwohl wir in der Lage sind, TLog-Backups zu erstellen, vermute ich, dass wir auf ähnliche Funktionen dieses behobenen Fehlers stoßen. Daher plane ich, heute ein Ticket mit MS zu diesem speziellen Problem zu eröffnen.

Abhängig vom Ergebnis dieses Tickets werde ich versuchen, einen Call-Stack-Trace gemäß Joes Vorschlag zu erfassen und zu sehen, wohin wir gehen.

Endgültiges Update (Problem selbst behoben)

Nachdem die 52-Stunden-Marke der offenen Query Store-Transaktion (wie oben angegeben) überschritten wurde, entschied sich die AG für ein automatisches Failover. Bevor dies geschah, habe ich einige zusätzliche Metriken abgerufen. Über diesen von Sean bereitgestellten Link verfügte die betreffende Datenbank über einen sehr großen Versionsspeicher, der dieser Datenbank gewidmet war, insbesondere an einem Punkt, an dem ich 1651360 Seiten im reserved_page_countFeld und 13210880 für den reserved_space_kbWert aufgezeichnet hatte .

Gemäß den ERRORLOGs trat das Failover nach einer 5-minütigen Flut von Transaktionshärtungsfehlern im Zusammenhang mit QDS base transactionund QDS nested transactionTransaktionen auf.

Das Failover verursachte in meinem Fall einen Ausfall von etwa 10 Minuten. Die Datenbank hat eine Größe von ~ 6 TB und ist sehr aktiv, was meiner Meinung nach eigentlich ziemlich gut war. Während der neuen Primärknoten während dieser Zeit online war, konnten keine Clientabfragen abgeschlossen werden, da alle auf den QDS_LOADDBWartetyp warteten .

Nach dem Failover wurden die Versionsspeichernummern auf 176 für reserved_page_countund 1408 für reduziert reserved_space_kb. Abfragen für die sekundären schreibgeschützten Replikate wurden ebenfalls so schnell ausgeführt, als ob sie vom primären ausgeführt würden. Es sieht also so aus, als wäre das Verhalten infolge des Failovers vollständig verschwunden.

John Eisbrener
quelle
Wenn Sie die Länge offener Transaktionen auf der Primärseite nicht ändern oder schwer abfragende Abfragen auf der Sekundärseite steuern können, sollte das Verweisen der Arbeitslast auf die Primärdaten das Problem mit langer Laufzeit verringern - dies könnte jedoch andere typische abfragebezogene Probleme betreffen. Ich würde nicht sagen, dass es normal ist, Replikate als nicht lesbar festzulegen, um Dinge zu klären, aber es ist eine gute Technik zur Fehlerbehebung. Es hängt alles davon ab, ob Sie die zugrunde liegende Ursache oder nur das Symptom beheben können / wollen, wenn die Dinge schlecht werden.
Sean Gallardy
1
Hey, John - großartig, diese Frage zu beantworten. Ich wollte nur erwähnen, über QDS_LOADDB- wenn Sie dies in Zukunft vermeiden möchten, aber den Query Store weiterhin aktiviert lassen möchten, können Sie diese von Microsoft empfohlenen Trace-Flags verwenden . Insbesondere lässt 7752 Abfragen ausführen, bevor der Abfragespeicher initialisiert wurde (sodass Sie möglicherweise einige Abfragen verpassen, Ihre Datenbank jedoch aktiv ist).
Josh Darnell
John - Gibt es eine Chance, dass Ihre Arbeitslast nicht parametrisiert, schlecht parametrisiert oder stark ad hoc ist? Ich habe ein paar Probleme mit QDS im Zusammenhang mit Ad-hoc-Workloads gesehen
AMtwo
@AMtwo Ja, ein Großteil der Abfragen, die in die Datenbank gelangen, wird auf dem Client generiert und nicht parametrisiert (dh Ad-hoc-Abfragen).
John Eisbrener
@JoshDarnell Trace-Flag 7752sieht besonders nützlich aus. Danke für den Tipp!
John Eisbrener

Antworten:

9

Diese Antwort ist zusätzlich zu Joes Antwort, da ich nicht 100% sicher sein kann, dass es sich um den Versionsspeicher handelt. Bisher gibt es jedoch genügend Beweise, um darauf hinzuweisen, dass dies Teil des Problems ist.

Wenn ein sekundäres Replikat als lesbar markiert ist, muss zuerst ein guter stabiler Zustand für die Versionsinformation erreicht werden, damit ein bekannter und guter Ausgangspunkt für alle Lesevorgänge auf dem sekundären Replikat vorhanden ist. Wenn dies auf den Übergang wartet und noch offene Transaktionen auf der Primärdatenbank vorhanden sind, wird dies als HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONINGein guter Indikator dafür angezeigt, dass die Primärdatenbank eine ziemliche Datenabwanderung durchläuft (oder zumindest jemand eine wirklich lange offene Transaktion hat, die ist auch nicht gut). Je länger die Transaktionen geöffnet sind und je mehr Daten geändert werden, desto mehr Versionen werden erstellt.

Sekundäre Replikate erreichen einen lesbaren Status, indem sie die Snapshot-Isolation unter der Abdeckung für die Sitzung verwenden. Wenn Sie jedoch Ihre Sitzungsinformationen überprüfen, wird diese beim festgeschriebenen Standardlesevorgang angezeigt. Da die Isolierung von Snapshots optimistisch ist und den Versionsspeicher verwendet, müssen alle Änderungen versioniert werden. Dies wird noch verstärkt, wenn auf der Sekundärseite viele Abfragen ausgeführt werden (und möglicherweise lange ausgeführt werden), während auf der Primärseite die Datenabwanderung hoch ist. Im Allgemeinen manifestiert sich dies nur in wenigen Tabellen für ein OLTP-System, es ist jedoch vollständig anwendungs- und arbeitslastabhängig.

Der Versionsspeicher selbst wird in Generationen gemessen. Wenn eine Abfrage ausgeführt wird, die die Verwendung des Versionsspeichers erfordert, wird der Zeiger für den Versionsdatensatz verwendet, um auf die TempDB-Kette dieser Zeile zu verweisen. Ich sage Kette, weil es eine Liste von Versionen für diese Zeile ist und die gesamte Kette nacheinander durchlaufen werden muss, um die richtige Version basierend auf dem Startzeitstempel der Transaktion zu finden, damit die Ergebnisse mit den Daten zu diesem bestimmten Zeitpunkt übereinstimmen.

Wenn der Versionsspeicher aufgrund lang laufender Transaktionen auf den primären und sekundären Replikaten viele Generationen für diese Zeilen hat, führt dies dazu, dass Abfragen länger als der Durchschnitt ausgeführt werden und im Allgemeinen in Form einer höheren CPU ausgeführt werden, während alle anderen Elemente scheinbar genau gleich bleiben - wie Ausführungsplan, Statistiken, zurückgegebene Zeilen usw. Das Gehen der Kette ist fast eine reine CPU-Operation. Wenn also die Ketten sehr lang werden und die Anzahl der zurückgegebenen Zeilen sehr hoch ist, erhalten Sie eine (nicht linear, aber kann nahe sein) Verlängerung der Zeit für die Abfrage.

Das einzige, was getan werden kann, ist, die Länge der Transaktionen sowohl auf der primären als auch auf der sekundären zu begrenzen, um sicherzustellen, dass der Versionsspeicher in TempDB bei vielen Generationen nicht zu groß wird. Versuche, den Versionsspeicher zu bereinigen, finden ungefähr einmal pro Minute statt. Für die Bereinigung müssen jedoch nicht mehr alle Versionen derselben Generation benötigt werden, bevor sie entfernt werden können, und alle zukünftigen Versionen können erst bereinigt werden, wenn die älteste Version nicht mehr benötigt wird. Daher kann eine lange laufende Abfrage dazu führen, dass viele nicht verwendete Generationen nicht effektiv bereinigt werden können.

Durch das Ein- und Ausschalten des Replikats in den lesbaren Modus wird auch der Versionsspeicher gelöscht, da er nicht mehr lesbar ist.

Es gibt andere Elemente, die ebenfalls im Spiel sein könnten, aber dies scheint angesichts der aktuellen Daten und der Art und Weise, wie das Replikat reagierte, am plausibelsten zu sein.

TempDB-Versionierung DMVs (nicht zu verwechseln mit ADR-Versionierung).

Sean Gallardy
quelle
Wenn Sie eine Abfrage für sys.dm_tran_version_store_space_usageausführen, wird 1651360 als mein reservierter_Seiten_Zahl und 13210880 als mein reservierter_Speicher_kb-Wert für die betreffende Datenbank zurückgegeben. Die Anzeigen sehen gut aus. Sie haben dieses Problem jedoch erkannt. Nochmals vielen Dank für die ausführliche Erklärung!
John Eisbrener
1
Ich bin zu 103% sicher, dass Sie das Problem richtig angerufen haben. Ich habe die Frage mit einigen Updates aktualisiert, aber vielen Dank für Ihre Erkenntnisse!
John Eisbrener
8

Haftungsausschluss: Ich weiß nichts über Verfügbarkeitsgruppen, aber ich weiß etwas über die Fehlerbehebung bei Abfragen, die anscheinend mehr CPU verbrauchen, als sie sollten.

Sie haben ein CPU-Problem, weil Sie zu viel davon verwenden. Eine wichtige Sache, die Sie über Wartezeiten sagen sollten, ist, dass fast alle nicht überlastet sind. Wenn ein Worker in einen Wartezustand wechselt, hat er nachgegeben und wird in SQLOS nicht mehr auf dem Scheduler ausgeführt. Wenn Sie also eine MAXDOP 1-Abfrage mit den folgenden Ausführungsstatistiken haben:

CPU-Zeit = 55719 ms, verstrichene Zeit = 56335 ms.

Sie haben eine CPU-Auslastung von fast 99% für die Abfrage erreicht. Warum sollte es für diese Abfrage aussagekräftige Wartestatistiken geben? Möglicherweise werden einige davon angezeigt, wenn einige der CPUs beschäftigt sind, z. B. externe oder vorbeugende Wartezeiten. Dies ist jedoch auch nicht garantiert. Das Fazit ist, dass Wartestatistiken hier möglicherweise nicht so hilfreich sind.

Es gibt einige Dinge, die in grober Reihenfolge überprüft werden müssen (die Reihenfolge hängt davon ab, was Sie über die Umgebung wissen):

  • Verfügt der sekundäre Server über eine teure Überwachung wie erweiterte Ereignisse, Ablaufverfolgungen oder Profilerstellung?
  • Entspricht die Hardware des sekundären Servers in etwa der primären?
  • Gibt es Konfigurations- oder Softwareprobleme mit dem sekundären Server?
  • Irgendwelche signifikanten Wartezeiten oder Verriegelungen? Möglicherweise nicht auf Ihre Anfrage anwendbar, kann aber dennoch Hinweise liefern.
  • Irgendwelche signifikanten Spinlocks?
  • Gibt es andere DMVs oder Dinge, die Sie in SQL Server überprüfen können und die möglicherweise Hinweise geben? Sie haben erwähnt, dass Verfügbarkeitsgruppen wahrscheinlich ein wesentlicher Bestandteil des Problems sind.
  • Was sagt Ihnen die ETW-Rückverfolgung?
  • Welche Art von Supportvereinbarungen haben Sie?

Die meisten der oben genannten Themen sind in verschiedenen Blog-Posts und Dokumentationen gut behandelt, aber ich werde die ETW-Ablaufverfolgung erweitern. Wenn Sie wissen möchten, warum SQL Server für eine bestimmte Abfrage so viel CPU verwendet und Sie Zugriff auf den Host haben, können Sie jederzeit eine ETW-Ablaufverfolgung durchführen, um Callstacks anzuzeigen und festzustellen, wie viel CPU verschiedene Callstacks leisten. Mit anderen Worten, das Host-Betriebssystem teilt Ihnen gerne mit, für welche CPU verwendet wird, wenn Sie wissen, wie Sie fragen sollen. Zu den gängigen Methoden zur ETW-Ablaufverfolgung gehören Windows Performance Recorder und PerfView .

Um diese Ergebnisse zu verstehen, sind fundierte interne Kenntnisse erforderlich, und es ist leicht, zu einer falschen Schlussfolgerung zu gelangen. In vielen Fällen ist es am besten, die Rohdaten zu sammeln und Experten zu bitten, sie anzusehen. Wenn Sie einen Trace ausführen, möchten Sie, dass in SQL Server so wenig Aktivität wie möglich ausgeführt wird. Im Folgenden finden Sie einige Antworten, die mithilfe der ETW-Ablaufverfolgung Schlussfolgerungen zu SQL Server ziehen:

Ich vermute, dass Sie in Ihrem Fall, wenn Sie Callstacks sammeln können, während die 45-Sekunden-Abfrage ausgeführt wird, einige sehr hilfreiche Hinweise auf die Art des Problems erhalten.

Joe Obbish
quelle
5

Da sich das Problem selbst gelöst hat, muss ich über seine Ursache spekulieren (Reim nicht beabsichtigt). Aufgrund von Seans Beitrag und der Tatsache, dass eine offene Query Store-Transaktion die Hauptursache für meine erhöhte Versionsspeichergröße zu sein scheint (z. B. die Ursache für die HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONINGWartezeiten), kann ich nur davon ausgehen, dass der Query Store einen Teil des Verhaltens hatte, das war vorgeführt. Diese Datenbank ist größer (~ 6 TB), ziemlich aktiv, und ein Großteil der Abfragen, die sie treffen, wird auf dem Client generiert und nicht parametrisiert (dh Ad-hoc-Abfragen). Daher glaube ich nicht, dass sich der Abfragespeicher für die Bereitstellung eignet viel Verwendung in diesem Szenario. Aus diesem Grund werden wir den Abfragespeicher in dieser Umgebung während eines zukünftigen Wartungsfensters deaktivieren. Danach werden wir dieses Verhalten vermutlich nicht mehr sehen.

Wir haben ein Ticket bei Microsoft eröffnet, aber das Timing war nicht zu unseren Gunsten, da das Problem behoben wurde, bevor wir eine detaillierte Analyse über einen PSSDIAG-Trace oder ähnliches durchführen konnten. Ich hoffe, dass sie in der Lage sind, einige lokalisierte Tests durchzuführen und dieses Problem zu replizieren, falls dies ein Fehler ist, auf den wir gestoßen sind. Wenn weitere Aktualisierungen zu einer dauerhafteren Lösung festgestellt werden, werde ich diese Antwort auch aktualisieren.

John Eisbrener
quelle