Behandeln von Problemen mit langsam ausgeführten Abfragen in SQL Server
Ursprüngliche Produktversion: SQL Server
Ursprüngliche KB-Nummer: 243589
Einführung
In diesem Artikel wird beschrieben, wie Sie ein Leistungsproblem behandeln, das bei Datenbankanwendungen auftreten kann, wenn sie SQL Server verwenden: langsame Leistung einer bestimmten Abfrage oder Gruppe von Abfragen. Die folgende Methodik hilft Ihnen, die Ursache für das Problem mit langsamen Abfragen einzugrenzen und Sie auf die Lösung zu leiten.
Suchen nach langsamen Abfragen
Um festzustellen, dass Abfrageleistungsprobleme in Ihrer SQL Server-Instanz auftreten, untersuchen Sie zunächst Abfragen anhand ihrer Ausführungszeit (verstrichene Zeit). Überprüfen Sie, ob die Zeit einen von Ihnen festgelegten Schwellenwert (in Millisekunden) basierend auf einer festgelegten Leistungsbasislinie überschreitet. In einer Stresstestumgebung haben Sie beispielsweise einen Schwellenwert für Ihre Workload festgelegt, der nicht mehr als 300 ms beträgt, und Sie können diesen Schwellenwert verwenden. Anschließend können Sie alle Abfragen identifizieren, die diesen Schwellenwert überschreiten, wobei sie sich auf jede einzelne Abfrage und deren vordefinierte Leistungsbasisplandauer konzentrieren. Letztendlich kümmern sich Geschäftsbenutzer um die Gesamtdauer von Datenbankabfragen; Daher liegt der Schwerpunkt auf der Ausführungsdauer. Andere Metriken wie CPU-Zeit und logische Lesevorgänge werden gesammelt, um die Untersuchung einzugrenzen.
Überprüfen Sie für derzeit ausgeführte Anweisungen total_elapsed_time und cpu_time Spalten in sys.dm_exec_requests. Führen Sie die folgende Abfrage aus, um die Daten abzurufen:
SELECT req.session_id , req.total_elapsed_time AS duration_ms , req.cpu_time AS cpu_time_ms , req.total_elapsed_time - req.cpu_time AS wait_time , req.logical_reads , SUBSTRING (REPLACE (REPLACE (SUBSTRING (ST.text, (req.statement_start_offset/2) + 1, ((CASE statement_end_offset WHEN -1 THEN DATALENGTH(ST.text) ELSE req.statement_end_offset END - req.statement_start_offset)/2) + 1) , CHAR(10), ' '), CHAR(13), ' '), 1, 512) AS statement_text FROM sys.dm_exec_requests AS req CROSS APPLY sys.dm_exec_sql_text(req.sql_handle) AS ST ORDER BY total_elapsed_time DESC;
Überprüfen Sie bei früheren Ausführungen der Abfrage last_elapsed_time und last_worker_time Spalten in sys.dm_exec_query_stats. Führen Sie die folgende Abfrage aus, um die Daten abzurufen:
SELECT t.text, (qs.total_elapsed_time/1000) / qs.execution_count AS avg_elapsed_time, (qs.total_worker_time/1000) / qs.execution_count AS avg_cpu_time, ((qs.total_elapsed_time/1000) / qs.execution_count ) - ((qs.total_worker_time/1000) / qs.execution_count) AS avg_wait_time, qs.total_logical_reads / qs.execution_count AS avg_logical_reads, qs.total_logical_writes / qs.execution_count AS avg_writes, (qs.total_elapsed_time/1000) AS cumulative_elapsed_time_all_executions FROM sys.dm_exec_query_stats qs CROSS apply sys.Dm_exec_sql_text (sql_handle) t WHERE t.text like '<Your Query>%' -- Replace <Your Query> with your query or the beginning part of your query. The special chars like '[','_','%','^' in the query should be escaped. ORDER BY (qs.total_elapsed_time / qs.execution_count) DESC
Notiz
Wenn
avg_wait_time
ein negativer Wert angezeigt wird, handelt es sich um eine parallele Abfrage.Wenn Sie die Abfrage bei Bedarf in SQL Server Management Studio (SSMS) oder Azure Data Studio ausführen können, führen Sie sie mit SET STATISTICS TIME
ON
und SET STATISTICS IOON
aus.SET STATISTICS TIME ON SET STATISTICS IO ON <YourQuery> SET STATISTICS IO OFF SET STATISTICS TIME OFF
Dann sehen Sie aus Nachrichten die CPU-Zeit, die verstrichene Zeit und logische Lesevorgänge wie folgt:
Table 'tblTest'. Scan count 1, logical reads 3, physical reads 0, page server reads 0, read-ahead reads 0, page server read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob page server reads 0, lob read-ahead reads 0, lob page server read-ahead reads 0. SQL Server Execution Times: CPU time = 460 ms, elapsed time = 470 ms.
Wenn Sie einen Abfrageplan sammeln können, überprüfen Sie die Daten aus den Eigenschaften des Ausführungsplans.
Führen Sie die Abfrage mit "Ist-Ausführungsplan einschließen" aus.
Wählen Sie den am weitesten links angeordneten Operator aus dem Ausführungsplan aus.
Erweitern Sie in "Properties" die QueryTimeStats-Eigenschaft.
Überprüfen Sie "ElapsedTime " und "CpuTime".
Ausführen im Vergleich zu Warten: Warum sind Abfragen langsam?
Wenn Sie Abfragen finden, die Ihren vordefinierten Schwellenwert überschreiten, überprüfen Sie, warum sie langsam sein könnten. Die Ursache von Leistungsproblemen kann in zwei Kategorien unterteilt werden:
WARTEN: Abfragen können langsam sein, da sie lange auf einen Engpass warten. Eine detaillierte Liste von Engpässen in Denkarten finden Sie unter "Waits".See a detailed list of bottlenecks in types of Waits.
AUSFÜHREN: Abfragen können langsam sein, da sie lange ausgeführt werden (ausführen). Mit anderen Worten: Diese Abfragen verwenden aktiv CPU-Ressourcen.
Eine Abfrage kann in ihrer Lebensdauer eine gewisse Zeit ausgeführt werden und eine gewisse Zeit warten. Ihr Fokus liegt jedoch darin, zu bestimmen, welche die dominante Kategorie ist, die zu ihrer langen verstrichenen Zeit beiträgt. Daher müssen Sie zunächst ermitteln, in welche Kategorie die Abfragen fallen. Es ist einfach: Wenn eine Abfrage nicht ausgeführt wird, wartet sie. Im Idealfall verbringt eine Abfrage den größten Teil ihrer verstrichenen Zeit in einem ausgeführten Zustand und sehr wenig Zeit beim Warten auf Ressourcen. Im besten Fall wird eine Abfrage auch innerhalb oder unterhalb eines vordefinierten Basisplans ausgeführt. Vergleichen Sie die verstrichene Zeit und die CPU-Zeit der Abfrage, um den Problemtyp zu ermitteln.
Typ 1: CPU-gebunden (Runner)
Wenn die CPU-Zeit geschlossen ist, gleich oder höher als die verstrichene Zeit ist, können Sie sie als CPU-gebundene Abfrage behandeln. Wenn die verstrichene Zeit beispielsweise 3000 Millisekunden (ms) beträgt und die CPU-Zeit 2900 ms beträgt, bedeutet dies, dass der großteil der verstrichenen Zeit für die CPU aufgewendet wird. Dann können wir sagen, es ist eine CPU-gebundene Abfrage.
Beispiele für die Ausführung von (CPU-gebundenen) Abfragen:
Verstrichene Zeit (ms) | CPU-Zeit (ms) | Lesevorgänge (logisch) |
---|---|---|
3200 | 3000 | 300000 |
1080 | 1.000 | 20 |
Logische Lesevorgänge – Lesen von Daten-/Indexseiten im Cache – sind am häufigsten die Treiber der CPU-Auslastung in SQL Server. Es kann Szenarien geben, in denen die CPU-Verwendung aus anderen Quellen stammt: eine While-Schleife (in T-SQL oder einem anderen Code wie XProcs oder SQL-CRL-Objekte). Das zweite Beispiel in der Tabelle veranschaulicht ein solches Szenario, bei dem der Großteil der CPU nicht aus Lesevorgängen stammt.
Notiz
Wenn die CPU-Zeit größer als die Dauer ist, gibt dies an, dass eine parallele Abfrage ausgeführt wird. Mehrere Threads verwenden die CPU gleichzeitig. Weitere Informationen finden Sie unter "Parallele Abfragen – Runner" oder "Waiter".
Typ 2: Warten auf einen Engpass (Waiter)
Eine Abfrage wartet auf einen Engpass, wenn die verstrichene Zeit deutlich größer als die CPU-Zeit ist. Die verstrichene Zeit umfasst die Ausführung der Abfrage auf der CPU (CPU-Zeit) und die Zeit, die auf die Freigabe einer Ressource (Wartezeit) wartet. Wenn die verstrichene Zeit beispielsweise 2000 ms beträgt und die CPU-Zeit 300 ms beträgt, beträgt die Wartezeit 1700 ms (2000 - 300 = 1700). Weitere Informationen finden Sie unter "Waits Types".
Beispiele für Warteabfragen:
Verstrichene Zeit (ms) | CPU-Zeit (ms) | Lesevorgänge (logisch) |
---|---|---|
2.000 | 300 | 28000 |
10080 | 700 | 80.000 |
Parallele Abfragen – Runner oder Waiter
Parallele Abfragen verwenden möglicherweise mehr CPU-Zeit als die Gesamtdauer. Das Ziel der Parallelität besteht darin, mehreren Threads gleichzeitig die Ausführung von Teilen einer Abfrage zu ermöglichen. In einer Sekunde der Taktzeit kann eine Abfrage acht Sekunden CPU-Zeit verwenden, indem sie acht parallele Threads ausführen. Daher wird es schwierig, eine CPU-gebundene oder eine Warteabfrage basierend auf der verstrichenen Zeit und CPU-Zeitdifferenz zu ermitteln. Beachten Sie jedoch in der Regel die in den obigen beiden Abschnitten aufgeführten Grundsätze. Die Zusammenfassung lautet:
- Wenn die verstrichene Zeit viel größer als die CPU-Zeit ist, sollten Sie es als Warteprogramm betrachten.
- Wenn die CPU-Zeit viel größer als die verstrichene Zeit ist, sollten Sie es als Läufer betrachten.
Beispiele für parallele Abfragen:
Verstrichene Zeit (ms) | CPU-Zeit (ms) | Lesevorgänge (logisch) |
---|---|---|
1200 | 8100 | 850000 |
3080 | 12300 | 1.500.000 |
Allgemeine visuelle Darstellung der Methodik
Diagnostizieren und Auflösen von Abfragen im Wartezustand
Wenn Sie festgestellt haben, dass Ihre Interessenabfragen Waiters sind, besteht ihr nächster Schritt darin, sich auf die Behebung von Engpassproblemen zu konzentrieren. Wechseln Sie andernfalls zu Schritt 4: Diagnostizieren und Auflösen von ausgeführten Abfragen.
Um eine Abfrage zu optimieren, die auf Engpässe wartet, identifizieren Sie, wie lange die Wartezeit ist und wo der Engpass liegt (der Wartetyp). Nachdem der Wartetyp bestätigt wurde, reduzieren Sie die Wartezeit, oder beseitigen Sie die Wartezeit vollständig.
Um die ungefähre Wartezeit zu berechnen, subtrahieren Sie die CPU-Zeit (Arbeitszeit) von der verstrichenen Zeit einer Abfrage. In der Regel ist die CPU-Zeit die tatsächliche Ausführungszeit, und der verbleibende Teil der Lebensdauer der Abfrage wartet.
Beispiele für die Berechnung der ungefähren Wartezeit:
Verstrichene Zeit (ms) | CPU-Zeit (ms) | Wartezeit (ms) |
---|---|---|
3200 | 3000 | 200 |
7080 | 1.000 | 6080 |
Identifizieren des Engpasses oder Wartens
Führen Sie die folgende Abfrage aus, >um historische Langewarteabfragen zu identifizieren (z. B. 20 % der gesamt verstrichenen Wartezeit). Diese Abfrage verwendet Seit dem Start von SQL Server Leistungsstatistiken für zwischengespeicherte Abfragepläne.
SELECT t.text, qs.total_elapsed_time / qs.execution_count AS avg_elapsed_time, qs.total_worker_time / qs.execution_count AS avg_cpu_time, (qs.total_elapsed_time - qs.total_worker_time) / qs.execution_count AS avg_wait_time, qs.total_logical_reads / qs.execution_count AS avg_logical_reads, qs.total_logical_writes / qs.execution_count AS avg_writes, qs.total_elapsed_time AS cumulative_elapsed_time FROM sys.dm_exec_query_stats qs CROSS apply sys.Dm_exec_sql_text (sql_handle) t WHERE (qs.total_elapsed_time - qs.total_worker_time) / qs.total_elapsed_time > 0.2 ORDER BY qs.total_elapsed_time / qs.execution_count DESC
Führen Sie die folgende Abfrage aus, um derzeit ausgeführte Abfragen mit einer Wartezeit von mehr als 500 ms zu identifizieren:
SELECT r.session_id, r.wait_type, r.wait_time AS wait_time_ms FROM sys.dm_exec_requests r JOIN sys.dm_exec_sessions s ON r.session_id = s.session_id WHERE wait_time > 500 AND is_user_process = 1
Wenn Sie einen Abfrageplan sammeln können, überprüfen Sie die WaitStats aus den Ausführungsplaneigenschaften in SSMS:
- Führen Sie die Abfrage mit "Ist-Ausführungsplan einschließen" aus.
- Klicken Sie mit der rechten Maustaste auf den operator ganz links auf der Registerkarte "Ausführungsplan "
- Wählen Sie "Eigenschaften" und dann "WaitStats "-Eigenschaft aus.
- Überprüfen Sie die WaitTimeMs und WaitType.
Wenn Sie mit PSSDiag/SQLdiag oder SQL LogFinder LightPerf/GeneralPerf-Szenarien vertraut sind, sollten Sie eine dieser Szenarien verwenden, um Leistungsstatistiken zu sammeln und Wartende Abfragen auf Ihrer SQL Server-Instanz zu identifizieren. Sie können die gesammelten Datendateien importieren und die Leistungsdaten mit SQL Nexus analysieren.
Verweise zur Beseitigung oder Reduzierung von Wartezeiten
Die Ursachen und Auflösungen für jeden Wartetyp variieren. Es gibt keine allgemeine Methode zum Auflösen aller Wartetypen. Im Folgenden finden Sie Artikel zur Problembehandlung und Behebung häufig auftretender Wartetypprobleme:
- Verstehen und Beheben von Blockierungsproblemen (LCK_M_*)
- Verstehen und Beheben von Problemen durch Blockierungen in Azure SQL-Datenbank
- Behandeln von langsamen SQL Server-Leistung durch E/A-Probleme (PAGEIOLATCH_*, WRITELOG, IO_COMPLETION, BACKUPIO)
- Konflikt beim Einfügen von PAGELATCH_EX auf letzter Seite in SQL Server auflösen
- Arbeitsspeicher gewährt Erläuterungen und Lösungen (RESOURCE_SEMAPHORE)
- Problembehandlung für langsame Abfragen, die aus ASYNC_NETWORK_IO Wartetyp resultieren
- Problembehandlung bei HADR_SYNC_COMMIT Wartetyp mit AlwaysOn-Verfügbarkeitsgruppen
- Funktionsweise: CMEMTHREAD und Debuggen
- Parallelität wird mit Aktionen gewartet (CXPACKET und CXCONSUMER)
- THREADPOOL-Wartezeit
Beschreibungen vieler Wait-Typen und deren Angabe finden Sie in der Tabelle unter "Waits".
Diagnostizieren und Auflösen von Abfragen im Ausführungszustand
Wenn die CPU-Zeit (Worker) sehr nahe an der gesamt verstrichenen Dauer liegt, verbringt die Abfrage den größten Teil der Ausführungsdauer. Wenn das SQL Server-Modul eine hohe CPU-Auslastung steuert, kommt die hohe CPU-Auslastung aus Abfragen, die eine große Anzahl logischer Lesevorgänge steuern (der häufigste Grund).
Führen Sie die folgende Anweisung aus, um die Abfragen zu identifizieren, die derzeit für hohe CPU-Aktivitäten verantwortlich sind:
SELECT TOP 10 s.session_id,
r.status,
r.cpu_time,
r.logical_reads,
r.reads,
r.writes,
r.total_elapsed_time / (1000 * 60) 'Elaps M',
SUBSTRING(st.TEXT, (r.statement_start_offset / 2) + 1,
((CASE r.statement_end_offset
WHEN -1 THEN DATALENGTH(st.TEXT)
ELSE r.statement_end_offset
END - r.statement_start_offset) / 2) + 1) AS statement_text,
COALESCE(QUOTENAME(DB_NAME(st.dbid)) + N'.' + QUOTENAME(OBJECT_SCHEMA_NAME(st.objectid, st.dbid))
+ N'.' + QUOTENAME(OBJECT_NAME(st.objectid, st.dbid)), '') AS command_text,
r.command,
s.login_name,
s.host_name,
s.program_name,
s.last_request_end_time,
s.login_time,
r.open_transaction_count
FROM sys.dm_exec_sessions AS s
JOIN sys.dm_exec_requests AS r ON r.session_id = s.session_id CROSS APPLY sys.Dm_exec_sql_text(r.sql_handle) AS st
WHERE r.session_id != @@SPID
ORDER BY r.cpu_time DESC
Wenn die CPU im Moment nicht durch Abfragen belastet wird, können Sie die folgende Anweisung ausführen, um nach historischen, CPU-gebundenen Abfragen zu suchen:
SELECT TOP 10 qs.last_execution_time, st.text AS batch_text,
SUBSTRING(st.TEXT, (qs.statement_start_offset / 2) + 1, ((CASE qs.statement_end_offset WHEN - 1 THEN DATALENGTH(st.TEXT) ELSE qs.statement_end_offset END - qs.statement_start_offset) / 2) + 1) AS statement_text,
(qs.total_worker_time / 1000) / qs.execution_count AS avg_cpu_time_ms,
(qs.total_elapsed_time / 1000) / qs.execution_count AS avg_elapsed_time_ms,
qs.total_logical_reads / qs.execution_count AS avg_logical_reads,
(qs.total_worker_time / 1000) AS cumulative_cpu_time_all_executions_ms,
(qs.total_elapsed_time / 1000) AS cumulative_elapsed_time_all_executions_ms
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(sql_handle) st
ORDER BY(qs.total_worker_time / qs.execution_count) DESC
Häufige Methoden zum Auflösen von zeitintensiven CPU-gebundenen Abfragen
- Überprüfen des Abfrageplans der Abfrage
- Statistikaktualisierung
- Identifizieren Sie die fehlenden Indizes, und wenden Sie sie an. Weitere Schritte zum Identifizieren fehlender Indizes finden Sie unter Optimieren nicht gruppierter Indizes mit fehlenden Indexvorschlägen
- Neugestaltung oder Neuschreiben der Abfragen
- Identifizieren Sie parameterempfindliche Pläne, und beheben Sie die Probleme.
- Identifizieren und Beheben von SARG-Fähigkeitsproblemen
- Identifizieren und beheben Sie Zeilenzielprobleme , bei denen lange geschachtelte Schleifen durch TOP, EXISTS, IN, FAST, SET ROWCOUNT, OPTION (FAST N) verursacht werden können. Weitere Informationen finden Sie unter Row Goals Gone Rogue and Showplan Enhancements - Row Goal EstimateRowsWithoutRowGoal
- Bewerten und Beheben von Problemen bei der Kardinalitätsschätzung . Weitere Informationen finden Sie unter "Verringerte Abfrageleistung nach dem Upgrade von SQL Server 2012 oder früher auf 2014 oder höher"
- Identifizieren und Beheben von Quriesen, die scheinbar nicht vollständig erscheinen, siehe Problembehandlungsabfragen, die scheinbar nie in SQL Server enden
- Identifizieren und Beheben von langsamen Abfragen, die durch Optimierertimeout betroffen sind
- Ermitteln von Problemen mit hoher CPU-Leistung. Weitere Informationen finden Sie unter Behandeln von Problemen mit hoher CPU-Auslastung in SQL Server
- Problembehandlung bei einer Abfrage, die einen erheblichen Leistungsunterschied zwischen zwei Servern zeigt
- Erhöhen Sie die Computingressourcen auf dem System (CPUs).
- Behandeln von UPDATE-Leistungsproblemen mit schmalen und breiten Plänen
Empfohlene Ressourcen
- Erkennbare Typen von Engpässen bei der Abfrageleistung in SQL Server und Azure SQL Managed Instance
- Tools für die Leistungsüberwachung und -optimierung
- Automatische Optimierung
- Allgemeine Richtlinien zum Indexentwurf
- Behandeln von Problemen mit dem Abfragetimeout
- Behandeln von Problemen mit hoher CPU-Auslastung in SQL Server
- Verringerte Abfrageleistung nach dem Upgrade von SQL Server 2012 oder früher auf 2014 oder höher