SQL Server-Anweisungen werden in SQL Server 2008 R2 zeitweise langsamer

13

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?

Dori
quelle

Antworten:

4

Vielen Dank für die ausführliche Erklärung Ihres Problems (eine der am besten gestellten Fragen überhaupt).

WRITELOG ist eine sehr häufige Art des Wartens. Machen Sie sich also keine Sorgen. Wenn Sie sich das Feld SOS_SCHEDULER_YIELD ansehen, das den CPU-Druck und auch das CXPACKET angibt, müssen möglicherweise einige Indizes fehlen, und Sie können viele Daten aus den Abfragen für ein OLTP-System abrufen. Ich empfehle Ihnen, sich die DMV mit den fehlenden Indizes anzuschauen und zu prüfen, ob es Indizes in den fraglichen Prozessen gibt (fast sicher, dass es mehr als nur wenige geben wird).

http://sqlfool.com/2009/04/a-look-at-missing-indexes/

http://troubleshootingsql.com/2009/12/30/how-to-find-out-the-missing-indexes-on-a-sql-server-2008-or-2005-instance-along-with-the- Erstelle-Index-Befehle /

Suchen Sie auch hier nach Jonathan Kehayias 'Beitrag auf sqlblog.com.

Schauen Sie sich auch Parameter Sniffing an.

http://sommarskog.se/query-plan-mysteries.html

http://pratchev.blogspot.com/2007/08/parameter-sniffing.html

Es ist KEINE konkurrierende Antwort auf Ihre Bedürfnisse, sondern ein guter Ausgangspunkt. Lassen Sie uns wissen, wenn Sie weitere Details benötigen.

Sankar Reddy
quelle
1

Wir hatten ein ähnliches Problem, nachdem einer der Mitarbeiter einige der gespeicherten Prozeduren neu geschrieben hatte. Es stellte sich heraus, dass übermäßige Verzweigungen und das Erstellen von Dynamic SQL die where-Klausel erheblich veränderten.

Zum Beispiel (natürlich vereinfacht):

Wenn das Modell "X" war, entspricht die where-Klausel für ProductCode bestimmten Werten.
Wenn das Modell "Y" war, stimmte die where-Klausel für ProductType mit bestimmten Werten überein .

SQL Server erstellt bei der ersten Ausführung der gespeicherten Prozedur einen Abfrageplan basierend auf den Eingabeparametern. Wenn der Abfrageplan also auf einer Logik basiert, die "ProductCode" gleich verwendet und Sie nach "ProductType" fragen, handelt es sich um einen nicht übereinstimmenden Abfrageplan, der höchstwahrscheinlich zu einem vollständigen Tabellenscan führt.

Sie können versuchen, " WITH RECOMPILE " an dem Anfang der gespeicherten Prozedur zu platzieren. ERSTELLUNGSVERFAHREN (Transact-SQL)

Ich kann das am besten wie folgt beschreiben:

Angenommen, Sie haben eine Liste mit Namen und Telefonnummern, die nach Nachnamen sortiert sind. Dies ist ideal, um Personen zu finden, die ihren Nachnamen verwenden (Abfrageplan basiert auf dem Nachnamen). Angenommen, Sie benötigen alle Namen und Telefonnummern in der Vorwahl 203. Wenn Ihre Liste nach Nachnamen sortiert ist, können Sie eine vollständige Liste aller Personen in der Vorwahl 203 nur erstellen, indem Sie von oben beginnen und nacheinander jedes und durchlesen jede Aufzeichnung. (Vollständiger Tabellenscan).

Michael Riley - AKA Gunny
quelle
Die Verwendung der exec()Funktion würde das beobachtete Verhalten erklären. In diesem Fall sp_executesqlbehebt die Verwendung von normalerweise die Probleme mit dynamischen SQL-Anweisungen.
Ajeh
1

Wenn die Abfragen in SSMS und der App zeitweise schnell und langsam ausgeführt werden, liegt möglicherweise ein Statistik- oder Parameter-Sniffing-Problem vor.

Ich führe diese gespeicherten Prozeduren aus und überprüfe dann den Ausführungsplan, um die Eigenschaften des Stammoperators (grüner Knoten ganz links von jeder Anweisung) abzurufen.

Wie viele Zeilen sind im Ausführungsplan voraussichtlich enthalten, verglichen mit der Anzahl der tatsächlich zurückgegebenen Zeilen?

Entspricht der kompilierte Parameter dem tatsächlichen Abfrageparameter?

Wenn der Ausführungsplan für einen Parameter erstellt wurde, der nur eine Handvoll Zeilen zurückgibt, und Sie dieselbe Prozedur mit einem Parameter ausführen, der eine große Anzahl von Zeilen zurückgibt, verwendet SQL möglicherweise den falschen Ausführungsplan für die Abfrage.

Die Auswahl des Ausführungsplans hängt eng mit den SQL-Statistiken zusammen. Daher ist es eine gute Idee, Ihre Statistiken regelmäßig neu zu erstellen.

Wenn Sie über eine gespeicherte Prozedur verfügen, die manchmal kleine Datenmengen oder große Datenmengen zurückgibt, je nachdem, welcher Parameter angegeben wurde, liegt möglicherweise ein Parameter-Sniffing-Problem vor.

Wenn die Neuerstellung Ihrer Statistiken das Problem nicht löst, können Sie die teuerste (n) Anweisung (en) in der gespeicherten Prozedur mit ausführen OPTION (RECOMPILE)

Andre Ranieri
quelle
0

Nachdem Sie lange laufende Abfragen identifiziert haben, können Sie die Ausführungspläne für diese Prozeduren aus Ihrem Cache abrufen und prüfen, ob Sie das Problem dort ermitteln können. Oft gibt es implizite oder Laufzeitkonvertierungen von Datentypen. Wenn Sie viele Daten löschen oder einfügen, empfiehlt es sich, auch die Statistiken zu aktualisieren.

Chandan jha
quelle