Bei einem unserer Kunden gab es einige Leistungsprobleme bei unserer Anwendung. Es ist eine .NET 3.5-Webanwendung, die Daten in einer SQL Server-Datenbank verbraucht und aktualisiert. Derzeit besteht unsere Produktionsumgebung aus einem Windows 2008 R2-Computer als Front-End und einem SQL Server 2008 R2-Cluster als Back-End. Unsere App verwendet COM + und MSDTC, um eine Verbindung zur Datenbank herzustellen.
Folgendes passiert: Unsere Endbenutzer klagen manchmal über Langsamkeit in der Anwendung. Das Laden einiger Seiten dauert etwas länger als erwartet. Bei dem Versuch herauszufinden, was passiert, konnte ich auf der Datenbankseite ein merkwürdiges Verhalten feststellen, das möglicherweise die Ursache für den Leistungsabfall ist. Mir ist aufgefallen, dass es manchmal einige SQL-Anweisungen gibt, deren Ausführung viel länger dauert als erwartet. Ich konnte einige dieser Anweisungen (hauptsächlich Aufrufe einiger gespeicherter Prozeduren unserer Anwendung) mithilfe eines Profiler-Trace (mit TSQL_Duration-Vorlage) identifizieren, um die lang laufenden Abfragen zu identifizieren.
Das Problem ist, dass beim Ausführen dieser gespeicherten Prozeduren direkt in der Datenbank in SQL Management Studio manchmal lange (ca. 7/8 Sekunden) und manchmal schnelle (unter 1 Sekunde) dauern. Ich weiß nicht, warum das passiert, und es macht mich verrückt, weil der SQL-Computer (4-Kern, 32 GB) nicht von anderen Anwendungen verwendet wird und die Ausführung dieser Abfragen nicht so lange dauern sollte.
Da ich kein DBA oder SQL Server-Guru bin, habe ich versucht, mir einige Dinge anzuschauen, die mir helfen könnten, das Problem zu verstehen. Hier sind die Schritte, die ich unternommen habe, um das Problem zu lösen und was ich bisher herausgefunden habe:
- Der gesamte von der Anwendung aufgerufene TSQL-Code wird in gespeicherten Prozeduren geschrieben.
- Ich habe einige der Abfragen mit langer Laufzeit im SQL Server-Profiler identifiziert. Wenn ich sie jedoch in Management Studio ausführe, dauert die Ausführung entweder lange (von 4 bis 10 Sekunden) oder dauert schnell (unter 1 Sekunde). Ich führe genau dieselben Abfragen mit denselben Daten aus, die in den Parametern übergeben wurden. Diese Abfragen sind hauptsächlich gespeicherte Prozeduren mit ausgewählten Anweisungen.
- Ich habe versucht, anhand der Warteschlangen- und Warteschlangenstatistik herauszufinden, ob auf einigen Ressourcen Prozesse warten. Ich habe die folgende Abfrage ausgeführt:
WITH Waits AS
(SELECT
wait_type,
wait_time_ms / 1000.0 AS WaitS,
(wait_time_ms - signal_wait_time_ms) / 1000.0 AS ResourceS,
signal_wait_time_ms / 1000.0 AS SignalS,
waiting_tasks_count AS WaitCount,
100.0 * wait_time_ms / SUM (wait_time_ms) OVER() AS Percentage,
ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS RowNum
FROM sys.dm_os_wait_stats
WHERE wait_type NOT IN (
'CLR_SEMAPHORE', 'LAZYWRITER_SLEEP', 'RESOURCE_QUEUE', 'SLEEP_TASK',
'SLEEP_SYSTEMTASK', 'SQLTRACE_BUFFER_FLUSH', 'WAITFOR', 'LOGMGR_QUEUE',
'CHECKPOINT_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_TIMER_EVENT', 'BROKER_TO_FLUSH',
'BROKER_TASK_STOP', 'CLR_MANUAL_EVENT', 'CLR_AUTO_EVENT', 'DISPATCHER_QUEUE_SEMAPHORE',
'FT_IFTS_SCHEDULER_IDLE_WAIT', 'XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN', 'BROKER_EVENTHANDLER',
'TRACEWRITE', 'FT_IFTSHC_MUTEX', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
'BROKER_RECEIVE_WAITFOR', 'ONDEMAND_TASK_QUEUE', 'DBMIRROR_EVENTS_QUEUE',
'DBMIRRORING_CMD', 'BROKER_TRANSMITTER', 'SQLTRACE_WAIT_ENTRIES',
'SLEEP_BPOOL_FLUSH', 'SQLTRACE_LOCK')
)
SELECT
W1.wait_type AS WaitType,
CAST (W1.WaitS AS DECIMAL(14, 2)) AS Wait_S,
CAST (W1.ResourceS AS DECIMAL(14, 2)) AS Resource_S,
CAST (W1.SignalS AS DECIMAL(14, 2)) AS Signal_S,
W1.WaitCount AS WaitCount,
CAST (W1.Percentage AS DECIMAL(4, 2)) AS Percentage,
CAST ((W1.WaitS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgWait_S,
CAST ((W1.ResourceS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgRes_S,
CAST ((W1.SignalS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgSig_S
FROM Waits AS W1
INNER JOIN Waits AS W2 ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type, W1.WaitS, W1.ResourceS, W1.SignalS, W1.WaitCount, W1.Percentage
HAVING SUM (W2.Percentage) - W1.Percentage < 95; -- percentage threshold
GO
Folgendes habe ich herausgefunden:
- Nachdem ich die Statistiken mit DBCC SQLPERF zurückgesetzt habe (ungefähr 1 oder 2 Stunden später), sind die Wartetypen, die ich am häufigsten habe, SOS_SCHEDULER_YIELD und WRITELOG
- Im Zeitverlauf (nach ca. 1 Tag Ausführung) sind die in der Datenbank am häufigsten auftretenden Wartetypen CXPACKET (67%) und OLEDB (17%), obwohl die durchschnittliche Wartezeit für jeden nicht lang ist. Ich habe auch bemerkt, dass die länger laufenden Anweisungen, die in SQL Profiler identifiziert werden, Aufrufe von gespeicherten Prozeduren sind, die mehr als eine Ergebnismenge (oft 3) zurückgeben. Kann es hier ein Paralellismusproblem geben? Kann ich auf irgendeine Weise feststellen, ob dies die Ursache des Problems ist?
- Ich habe irgendwo gelesen, dass OLEDB-Wartezeiten durch Aufrufe von OLEDB-Ressourcen wie Verbindungsservern verursacht werden können. Wir haben zwar einen Verbindungsserver, um eine Verbindung mit einem Indexing Services-Computer (MSIDXS) herzustellen, aber keine der als langfristig identifizierten Anweisungen verwendet diesen Verbindungsserver.
- Die höhere durchschnittliche Wartezeit, die ich habe, gilt für Wartezeiten vom Typ LCK_M_X (durchschnittlich 1,5 Sekunden), aber diese Wartezeiten treten im Vergleich zu anderen Typen nicht sehr häufig auf (z. B. 64 Wartezeiten vom Typ LCK_M_X im Vergleich zu 10.823 Wartezeiten von CXPACKET im selben Zeitraum) ).
- Eine Sache, die mir aufgefallen ist, ist, dass der MSDTC-Dienst nicht geclustert ist. Der SQL Server-Dienst ist geclustert, MSDTC jedoch nicht. Kann es deswegen einen Performance-Hit geben? Wir verwenden MSDTC, da unsere App Enterprise Services (DCOM) für den Zugriff auf die Datenbank verwendet, die Server jedoch nicht von uns, sondern von unserem Client installiert und konfiguriert wurden.
Kann mir jemand helfen, diese Daten besser zu verstehen? Kann mir jemand helfen, das Geschehen zu verstehen? Gibt es etwas, was ich auf dem Server tun kann, um Dinge herauszufinden? Sollte ich mit dem Anwendungsentwicklungsteam sprechen?
exec()
Funktion würde das beobachtete Verhalten erklären. In diesem Fallsp_executesql
behebt die Verwendung von normalerweise die Probleme mit dynamischen SQL-Anweisungen.