SQL Server läuft langsam – die Diagnose-Pyramide
Vier Stufen, feste Reihenfolge – vom Gefühl zum Befund
SQL Server läuft langsam — die Diagnose-Pyramide
„Das System ist träge“ ist kein Befund, sondern ein Gefühl. So machst du in vier Stufen eine Diagnose daraus — ohne Schnellschüsse.
Es ist Mittwochmorgen, 08:51 Uhr. Das Telefon klingelt, am anderen Ende der Geschäftsführer persönlich: „Das System ist langsam. Machen Sie was.“ Auf die Rückfrage, was genau langsam sei, kommt die Antwort, die jeder DBA auswendig kennt: „Na, alles!“ Im Hintergrund hört man den Vertrieb stöhnen, weil die Angebotsmaske acht Sekunden zum Öffnen braucht — gestern waren es zwei. Kein Fehler im Log, kein roter Alarm im Monitoring, die CPU dümpelt bei 40 Prozent. Der Server ist nicht krank genug für die Notaufnahme, aber zu krank zum Arbeiten.
Willkommen beim Allzweck-Hilferuf des SQL-Server-Alltags. „Langsam“ ist kein Befund, sondern ein Gefühl — und genau das macht dieses Symptom so tückisch: Es gibt dutzende mögliche Ursachen, und fast alle Schnellschüsse (Neustart! Index-Rebuild! Mehr RAM!) behandeln das Gefühl statt der Krankheit. Dieser Beitrag aus dem SQL-Server-Notarztwagen (die Triage-Übersicht liegt unter /sql-server-notarztwagen/) gibt dir das Gegenmittel: die Diagnose-Pyramide. Vier Stufen, feste Reihenfolge, keine Panik — und am Ende ein Befund statt einer Vermutung.
Was bedeutet „langsam“ technisch?
Zuerst die Abgrenzung nach oben: Wenn gar nichts mehr geht — keine neuen Verbindungen, keine Antwort, auch nicht nach Minuten —, bist du nicht hier richtig, sondern beim ROT-Beitrag „SQL Server hängt vollständig“ mit seinem DAC-Notausgang. „Langsam“ heißt: Der Server arbeitet, aber er arbeitet schlechter als gestern. Und genau dieses „als gestern“ ist der erste technische Hebel — Langsamkeit ist immer relativ. Acht Sekunden für die Angebotsmaske sind nur deshalb ein Problem, weil es gestern zwei waren.
Der zweite Hebel ist eine unbequeme Wahrheit: Der SQL Server weiß, warum er langsam ist — er führt akribisch Buch darüber. Jedes Mal, wenn eine Arbeitseinheit nicht weiterarbeiten kann, protokolliert er den Grund als Wartetyp: Warten auf eine Sperre, auf einen Plattenzugriff, auf Arbeitsspeicher, auf einen freien Prozessor-Slot. Diese Wait Stats sind die Sprache, in der der Server seine Probleme erklärt — man muss sie nur abfragen und übersetzen, statt zu raten. Deshalb beginnt die Pyramide genau dort und nirgendwo sonst.
Der dritte Hebel ist die Frage nach dem Auslöser: Performance verschlechtert sich fast nie grundlos. Entweder hat sich etwas geändert — ein Release, ein Statistik-Update, das einen Ausführungsplan kippen ließ, ein neuer Report, gewachsene Datenmengen — oder etwas läuft gerade, das sonst nicht läuft: der Monatsabschluss, ein Import, eine vergessene Transaktion. Die Frage „Was ist seit gestern anders?“ ist keine Höflichkeitsfloskel, sondern Stufe null der Diagnose.
|
Abgrenzung: langsam vs. hängt Langsam — der Server antwortet, aber träge. Queries laufen, nur länger als üblich. Diagnose über die Pyramide in diesem Beitrag, mit Ruhe und Methode. |
|---|
Der Diagnose-Pfad: die Pyramide
Die Pyramide hat vier Stufen, und die Reihenfolge ist nicht verhandelbar: Jede Stufe verengt den Verdacht für die nächste. Wer bei Stufe vier einsteigt — also direkt an Indizes, Hints und Plänen schraubt —, optimiert ohne Befund und trifft bestenfalls zufällig.

Erste Diagnose: konkrete Skripte
Stufe 1 — Wait Stats: Worauf wartet der Server?
Das folgende Skript zeigt die Wartetypen mit dem größten Anteil an der Gesamtwartezeit — bereinigt um die gängigen Harmlosen, die nur Leerlauf protokollieren:
|
— Top-Wartetypen seit dem letzten Neustart (Harmloses gefiltert) SELECT TOP (15) wait_type, wait_time_ms / 1000 AS WaitSec, signal_wait_time_ms / 1000 AS SignalSec, waiting_tasks_count AS Anzahl, CAST(100.0 * wait_time_ms / SUM(wait_time_ms) OVER() AS decimal(5,2)) AS Prozent FROM sys.dm_os_wait_stats WHERE wait_type NOT IN ( N'SLEEP_TASK', N'LAZYWRITER_SLEEP', N'BROKER_TASK_STOP', N'BROKER_TO_FLUSH', N'SQLTRACE_BUFFER_FLUSH', N'WAITFOR', N'XE_TIMER_EVENT', N'XE_DISPATCHER_WAIT', N'CHECKPOINT_QUEUE', N'REQUEST_FOR_DEADLOCK_SEARCH', N'LOGMGR_QUEUE', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'DIRTY_PAGE_POLL', N'SP_SERVER_DIAGNOSTICS_SLEEP' ) AND wait_time_ms > 0 ORDER BY wait_time_ms DESC; |
|---|
Die Übersetzung der Spitzenreiter: LCK_M_* heißt Sperrkonflikte — weiter zum Blocking-Beitrag. PAGEIOLATCH_* heißt Warten auf Storage — Stufe drei wird spannend. CXPACKET und CXCONSUMER heißen Parallelität — meist ein Folgesymptom teurer Queries, also weiter zu Stufe zwei. RESOURCE_SEMAPHORE heißt Memory-Druck bei Abfrage-Grants, SOS_SCHEDULER_YIELD deutet auf CPU-Hunger. Eine Einschränkung hat die DMV: Sie zählt seit dem letzten Neustart — auf einem Server mit 200 Tagen Uptime versteckt sich das Problem von heute im Durchschnitt von sieben Monaten. Für die Akut-Analyse zweimal im Abstand von ein paar Minuten messen und die Differenz betrachten.
Stufe 2 — Plan-Cache: Wer frisst die Ressourcen?
|
— Top-10-Queries nach CPU (alternativ: total_logical_reads, — total_elapsed_time — je nach Befund aus Stufe 1) SELECT TOP (10) qs.total_worker_time / 1000 AS CpuMs, qs.execution_count AS Ausfuehrungen, qs.total_worker_time / qs.execution_count / 1000 AS CpuMsProLauf, qs.total_logical_reads AS Reads, 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 StatementText FROM sys.dm_exec_query_stats AS qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st ORDER BY qs.total_worker_time DESC; |
|---|
Sortiere nach dem, was Stufe eins nahegelegt hat: CPU-lastige Waits — nach total_worker_time. I/O-lastige Waits — nach total_logical_reads. Gefühlte Trägheit ohne klaren Ressourcen-Befund — nach total_elapsed_time, denn dort sammeln sich auch die Sperren-Opfer. In neun von zehn Fällen liefern die Top Ten zwei, drei alte Bekannte — und sehr oft ist es eine einzige Query, deren Plan nach einem Statistik-Update gekippt ist.
Stufe 3 — Ressourcen: antwortet das Fundament?
|
— I/O-Latenz pro Datenbankdatei (Richtwerte: Daten < 20 ms, Log < 5 ms) SELECT DB_NAME(vfs.database_id) AS Datenbank, mf.type_desc AS Dateityp, vfs.num_of_reads, CASE WHEN vfs.num_of_reads = 0 THEN 0 ELSE vfs.io_stall_read_ms / vfs.num_of_reads END AS ReadLatenzMs, CASE WHEN vfs.num_of_writes = 0 THEN 0 ELSE vfs.io_stall_write_ms / vfs.num_of_writes END AS WriteLatenzMs FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS vfs JOIN sys.master_files AS mf ON mf.database_id = vfs.database_id AND mf.file_id = vfs.file_id ORDER BY ReadLatenzMs DESC; |
|---|
Datendateien über 20 Millisekunden und Logdateien über 5 Millisekunden Lese- bzw. Schreiblatenz sind ein Storage-Gespräch wert — die Tiefenbohrung dazu liefert der I/O-Latenz-Beitrag. Für den Speicher lohnt der Blick auf die Page Life Expectancy im Perfmon: Stürzt sie regelmäßig ab, wird der Buffer Pool zu klein für die Arbeitslast — das Thema des Memory-Pressure-Beitrags. Erst wenn Stufe eins bis drei den Verdacht eingegrenzt haben, geht es auf Stufe vier: die konkreten Ausführungspläne der Top-Queries lesen, fehlende Indizes prüfen, Statistiken kontrollieren — jetzt mit Befund, nicht mit Bauchgefühl.
Die häufigsten Ursachen — und was du dagegen tust
1. Ein Plan ist gekippt — Parameter Sniffing und Plan-Regression
Der häufigste Grund für „gestern schnell, heute langsam“: Der Optimizer hat für eine zentrale Query einen neuen Ausführungsplan erstellt — nach einem Statistik-Update, einem Neustart oder schlicht, weil der alte Plan aus dem Cache gefallen ist — und der neue Plan ist für die typischen Parameterwerte eine Katastrophe. Erkennungszeichen in Stufe zwei: eine Query mit explodierter CpuMsProLauf bei unveränderter Ausführungszahl. Die Soforthilfe kann ein gezieltes UPDATE STATISTICS oder ein einmaliges Recompile sein; die strukturelle Lösung ist der Query Store mit Plan-Forcing — damit zwingst du den bewährten Plan zurück und hast künftige Regressionen im Report. Die ganze Tiefe dazu liefert der Statistiken-Beitrag des Notarztwagens.
2. Sperren — der Server wartet auf sich selbst
Dominieren LCK_M_*-Wartetypen die Stufe eins, ist der Server nicht überlastet, sondern blockiert: Irgendwo hält eine Transaktion ihre Sperren zu lange, und dahinter staut sich die Arbeit. Das fühlt sich für Anwender exakt wie „langsam“ an — ist aber ein völlig anderes Krankheitsbild als ein Ressourcen-Engpass, und mehr Hardware hilft dagegen exakt gar nicht. Die Kette aufrollen, den Head-Blocker finden, gezielt eingreifen: Das komplette Vorgehen samt KILL-Checkliste steht im Beitrag zu den Blocking-Kaskaden.
3. I/O-Engpass — das Storage antwortet zu langsam
Hohe PAGEIOLATCH-Waits plus Latenzen jenseits der Richtwerte aus Stufe drei: Der Server liest mehr von der Platte, als das Storage liefern kann. Vor dem Anruf beim Storage-Team aber die Gegenprobe: Liest er zu viel, weil Scans und fehlende Indizes unnötig Seiten anfassen? Oder ist der Buffer Pool zu klein, sodass Gelesenes sofort wieder verdrängt wird? Ein I/O-Problem kann drei Adressen haben — Storage, Memory, Code — und die Rechnung ohne diese Unterscheidung zu bezahlen, wird teuer. Schwellenwerte, Messmethodik und die Storage-Argumentation liefert der I/O-Latenz-Beitrag.
4. Memory Pressure — der Buffer Pool ist zu klein geworden
Schleichende Variante: Die Datenmenge wächst über Monate, der Arbeitsspeicher bleibt — und irgendwann passt das Arbeits-Set nicht mehr in den Buffer Pool. Symptome: absackende Page Life Expectancy, steigende PAGEIOLATCH-Waits (der Server muss ständig nachladen) und bei Abfrage-Grants RESOURCE_SEMAPHORE-Wartezeiten. Kurzfristig hilft die Kontrolle von Max Server Memory (steht es noch auf einem historischen Wert?), mittelfristig die Frage Hardware vs. Workload-Diät. Diagnose und Konfigurationsregeln im Detail: der Memory-Pressure-Beitrag der GELB-Stufe.
5. Der Server ist gar nicht schuld
Der unterschätzte Klassiker: ASYNC_NETWORK_IO an der Spitze der Wait Stats heißt in aller Regel nicht „Netzwerk kaputt“, sondern: Der Server hat die Ergebnisse längst fertig, aber die Anwendung holt sie zu langsam ab — weil sie Millionen Zeilen anfordert und clientseitig zeilenweise verarbeitet. Auch beliebt: Die Maske ist langsam, weil die Anwendung für einen Bildschirmaufbau vierzig Einzelabfragen feuert, jede für sich pfeilschnell. In beiden Fällen optimierst du am SQL Server vergeblich — der Befund gehört auf den Tisch des Anwendungsherstellers, und die Stufe-zwei-Zahlen sind deine Beweismittel.
Was du auf keinen Fall tun solltest
|
Die drei häufigsten Anfängerfehler 1. Bei Stufe vier einsteigen: an Indizes und Hints schrauben, bevor die Wait Stats überhaupt gelesen wurden. Das ist Therapie ohne Befund — bestenfalls Zufallstreffer. |
|---|
Nachsorge: Damit es nicht wiederkommt
Drei strukturelle Maßnahmen, sobald der akute Fall gelöst ist: Erstens, eine Baseline aufbauen — Wait Stats, Top-Queries und I/O-Latenzen regelmäßig wegschreiben. Beim nächsten „alles langsam“ vergleichst du gegen Normalwerte statt gegen Bauchgefühl; das verkürzt jede Diagnose um Stunden. Zweitens, den Query Store aktivieren, falls noch nicht geschehen — er ist das Gedächtnis für Pläne und Regressionen und macht aus „gestern war es schneller“ eine belegbare Aussage mit Plan-Vergleich. Drittens, einen regelmäßigen Health Check etablieren, der Konfiguration, Wartung und Baselines im Zusammenhang prüft — die komplette Prüfliste dafür liefert der Health-Check-Beitrag der GRÜN-Stufe.
Vertiefung in der Buchreihe
Die Pyramide ist das Gerüst — das Fleisch an den Knochen liefert die Buchreihe „SQL Server in der Praxis“, alle Bände in der Übersicht auf der SQL-Pillar-Seite unter /sql-server/.
|
Empfohlener Band Band 1: Performance & Troubleshooting — das Heimatbuch dieses Beitrags: Wait Stats in der ganzen Breite, Plan-Cache und Query Store, Sperren, Tempdb, I/O-Diagnose und Index-Strategien. Wer die Pyramide hier verstanden hat, findet dort jede Stufe als eigenes Kapitel mit Tiefgang. |
|---|
Verwandte Notfälle im Notarztwagen
„Langsam“ ist der Generalist unter den Symptomen — je nach Befund aus Stufe eins geht es hier weiter:
Drei Wege, wenn der Server jetzt gerade schleicht
Die Performance-Analyse zum Festpreis
Genau für dieses Symptom — „alles langsam, keiner weiß warum“ — gibt es meine Performance-Analyse zum Festpreis. Das Prinzip: Ich gehe die komplette Diagnose-Pyramide auf deinem System durch, professionell und mit den Werkzeugen aus dreißig Jahren Beratungspraxis — und du weißt vorher auf den Euro genau, was es kostet. Kein Stundenzähler, kein „wir schauen mal, wie lange es dauert“, keine Überraschung auf der Rechnung.
Konkret enthalten: die Wait-Stats-Auswertung mit Einordnung gegen typische Vergleichswerte, die Top-Query-Analyse aus Plan-Cache bzw. Query Store samt Blick in die kritischen Ausführungspläne, das I/O- und Memory-Profil deiner Instanz, ein Konfigurations-Check der üblichen Verdächtigen (Max Server Memory, MAXDOP, Cost Threshold, tempdb) — und als Ergebnis ein schriftlicher Bericht mit priorisierten Maßnahmen: sortiert nach Wirkung und Aufwand, getrennt in Quick-Wins und strukturelle Empfehlungen, in einer Sprache, die auch der Geschäftsführer aus der Lead-Szene versteht. Den Bericht besprechen wir gemeinsam, damit keine Maßnahme als Papiertiger endet.
Der ehrliche Nebeneffekt des Festpreises: Er zwingt zur Methode. Wer nach Aufwand abrechnet, kann sich Umwege leisten — wer zum Festpreis analysiert, geht die Pyramide diszipliniert von unten nach oben, weil das schlicht der schnellste Weg zum Befund ist. Du bekommst also genau das Vorgehen aus diesem Beitrag, nur mit Erfahrung, Vergleichswerten und Verantwortung.
Akut-Hotline
Wenn die Trägheit gerade jetzt das Tagesgeschäft würgt und keine Zeit für eine geordnete Analyse bleibt: anrufen. Halte einen Zugang mit VIEW-SERVER-STATE-Rechten bereit und die Antwort auf die Stufe-null-Frage — was ist seit gestern anders? Release, Datenimport, Stichtag? Mit den Pyramiden-Skripten ist die Richtung meist in der ersten halben Stunde klar.
Selbst-Diagnose-Kit
Du willst die Pyramide selbst durchsteigen? Sehr gute Idee — genau dafür ist dieser Beitrag geschrieben. Das Diagnose-Kit bündelt alle Skripte der vier Stufen als ausführbares Paket mit Auswertungshilfe, Richtwerten und einer Checkliste, welcher Befund zu welchem Notarztwagen-Beitrag führt. Damit gehst du den Weg in deinem Tempo — und weißt hinterher, wo es klemmt.
|
Ein letzter Tipp Führe das Wait-Stats-Skript aus diesem Beitrag heute aus — nicht beim nächsten Notfall, sondern jetzt, wo alles normal läuft. Schreib dir die Top fünf mit ihren Prozentwerten auf und leg den Zettel (oder die Tabelle) neben das Monitoring. Das ist deine Baseline für den Tag X: Wenn der Geschäftsführer das nächste Mal anruft, vergleichst du zwei Messungen statt zu raten — und bist allen einen Schritt voraus, die erst im Brandfall anfangen zu messen. |
|---|
Häufige Fragen (FAQ)
Diese Sektion ist auf häufige Suchanfragen abgestimmt. Sie hilft dir, schnell den Punkt zu finden, der dir gerade akut weiterhilft — und sie ist gleichzeitig die Eingabe für das FAQPage-Schema auf der Seite.
Warum ist mein SQL Server langsam geworden?
Die ehrliche Antwort: Das verrät dir der Server selbst — über seine Wait Stats. „Langsam“ hat dutzende mögliche Ursachen (Sperren, kippende Ausführungspläne, I/O-Engpässe, Memory Pressure, oder schlicht eine Anwendung, die Daten zu langsam abnimmt), und jede sieht in den Wartetypen anders aus. Deshalb beginnt jede seriöse Diagnose bei sys.dm_os_wait_stats statt beim Raten. Die häufigsten Auslöser plötzlicher Trägheit sind kippende Pläne nach Statistik-Updates, Blocking durch lange Transaktionen und Datenwachstum, das alte Indizes überfordert. Der systematische Weg steht in diesem Beitrag.
Wie diagnostiziere ich SQL-Server-Performance systematisch?
Mit der Diagnose-Pyramide, von unten nach oben: Stufe eins sind die Wait Stats — worauf wartet der Server überhaupt? Stufe zwei ist der Plan-Cache — welche Queries verbrauchen die meiste CPU, die meisten Reads, die meiste Zeit? Stufe drei sind die Ressourcen — I/O-Latenzen, Page Life Expectancy, CPU-Verteilung. Erst Stufe vier ist der Code: Ausführungspläne, Indizes, Statistiken. Die Reihenfolge ist der Punkt: Jede Stufe verengt den Verdacht, und wer oben anfängt, optimiert ins Blaue. Alle Skripte dazu findest du in diesem Beitrag.
Was sind Wait Stats?
Jedes Mal, wenn eine Arbeitseinheit im SQL Server nicht weiterarbeiten kann — weil sie auf eine Sperre, einen Festplattenzugriff, Arbeitsspeicher oder die CPU wartet —, protokolliert der Server den Grund als Wartetyp und summiert die Wartezeiten seit dem letzten Neustart. Diese Statistik in sys.dm_os_wait_stats ist die Sprache, in der dir der Server seine Engpässe erklärt: Dominieren LCK-Typen, hast du Sperrkonflikte; dominiert PAGEIOLATCH, wartet er auf Storage. Wait Stats sind deshalb immer Stufe eins der Diagnose. Die ausführliche Übersetzungshilfe liefert der Wait-Stats-Beitrag der GRÜN-Stufe.
Welche Wartetypen sollte ich ignorieren?
Eine ganze Reihe von Wartetypen ist schlicht Leerlauf oder interne Verwaltung und sagt nichts über Engpässe: Hintergrundprozesse, die planmäßig schlafen, Broker- und XE-Wartezyklen oder der berühmte WAITFOR. Klassische Vertreter sind unter anderem SLEEP_TASK, LAZYWRITER_SLEEP, BROKER_TASK_STOP, XE_TIMER_EVENT und REQUEST_FOR_DEADLOCK_SEARCH — der letzte ist nur der Deadlock-Monitor bei seinem Fünf-Sekunden-Rundgang. Das Wait-Stats-Skript in diesem Beitrag filtert die gängigen Harmlosen bereits heraus, damit du nicht versehentlich den Schlaf des Lazywriters therapierst.
Was bedeutet der Wartetyp PAGEIOLATCH_SH?
PAGEIOLATCH_SH heißt: Eine Session will eine Datenseite lesen, die noch nicht im Buffer Pool liegt — sie wartet darauf, dass die Seite von der Festplatte geladen wird. Vereinzelt ist das völlig normal. Dominiert der Typ aber die Wait Stats, liest dein Server zu viel von der Platte: entweder weil das Storage zu langsam antwortet, oder weil der Buffer Pool zu klein ist, oder weil Scans unnötig viele Seiten anfassen. Die Unterscheidung liefern Stufe zwei und drei der Pyramide — und für die Storage-Tiefe der I/O-Latenz-Beitrag des Notarztwagens.
Wie analysiere ich den Plan-Cache?
Über sys.dm_exec_query_stats: Die DMV führt für jeden gecachten Plan Buch über CPU-Zeit, Reads, Dauer und Ausführungszahl. Sortiere nach total_worker_time für die CPU-Fresser, nach total_logical_reads für die Lese-Monster und nach total_elapsed_time für die Langläufer — die Top Ten jeder Liste sind deine Kandidaten. Das fertige Skript steht im Diagnose-Abschnitt dieses Beitrags. Zwei Einschränkungen: Der Cache vergisst bei Neustart und Memory-Druck, und ad-hoc-lastige Systeme verwässern das Bild. Die dauerhafte Lösung heißt Query Store — Stichwort GRÜN-Stufe.
Wann ist ein Server-Restart sinnvoll?
Als Performance-Maßnahme: praktisch nie. Ein Neustart leert Buffer Pool und Plan-Cache — danach ist der Server erst einmal langsamer, weil er alles von der Platte nachladen und jeden Plan neu kompilieren muss. Vor allem aber löscht er deine Beweismittel: Die Wait Stats werden genullt, und die Diagnose beginnt bei null. Wenn ein Neustart „hilft“, hat er meist nur einen kippten Plan oder eine vergessene Transaktion entsorgt — beides ginge gezielt und ohne Kollateralschaden. Legitime Neustart-Gründe sind Patches, Konfigurationsänderungen, die ihn erfordern, und echte Prozess-Defekte.
