SQL Server: CPU dauerhaft bei 100 Prozent
Von der brennenden CPU zur Ursache – strukturiert in drei Diagnoseschichten.
CPU dauerhaft bei 100 Prozent
Alle Kerne brennen. Die Diagnose-Pyramide vom Top-Verursacher zum Query-Plan, die sieben häufigsten Brandstifter, Quick-Wins und Nachsorge.
Es ist Montagmorgen, 08:14 Uhr, bei der Musterwerk GmbH. Die erste Schicht hat sich angemeldet, die ersten Aufträge laufen ein — und das Warenwirtschaftssystem zäht wie Kaugummi. Ein Klick, der sonst eine halbe Sekunde braucht, dauert jetzt zwanzig. Der Blick auf den Datenbankserver erklärt alles und nichts zugleich: Alle Kerne stehen bei 100 Prozent, die CPU-Kurve im Task-Manager ist eine durchgezogene rote Linie an der Obergrenze. Der Server brennt. Und während die ersten Anrufe aus der Produktion kommen, lautet die Frage nicht „läuft die CPU heiß“, sondern „welche Abfrage hält das Streichholz dran“ — denn fast nie ist es die Hardware, fast immer ist es eine einzelne, entartete Abfrage.
Willkommen bei einem der häufigsten akuten Notfälle aus dem SQL-Server-Notarztwagen (die Triage-Übersicht liegt unter /sql-server-notarztwagen/). Eine CPU am Anschlag fühlt sich an wie Fieber: ein deutliches Symptom, aber noch keine Diagnose. Und genau wie beim Fieber bringt es nichts, blind am Thermostat zu drehen — man muss die Quelle finden. Dieser Beitrag zeigt dir die Diagnose-Pyramide: erst wer die CPU frisst, dann worauf er wartet, dann warum sein Plan so teuer ist. Plus die sieben häufigsten Brandstifter, die schnellen Löschmaßnahmen für den Moment — und den einen Reflex, der das Feuer oft nur größer macht.
Was „CPU bei 100 Prozent“ technisch bedeutet
Eine dauerhaft ausgelastete CPU heißt: Es gibt mehr rechenbereite Arbeit, als die Kerne in Echtzeit abarbeiten können. Beim SQL Server ist diese Arbeit fast immer das Ausführen von Abfragen — und die entscheidende Erkenntnis ist, dass nicht die Menge der Abfragen das Problem ist, sondern ihre Effizienz. Eine einzige schlecht geplante Abfrage, die statt eines gezielten Zugriffs Millionen Zeilen durchwühlt, kann einen ganzen Server in die Knie zwingen, während tausend gut geplante Abfragen ihn kaum kitzeln. Deshalb ist die erste mentale Weiche entscheidend: Es ist fast nie ein Hardware-Mangel und fast immer ein Effizienz-Problem. Wer beim CPU-Notfall zuerst an „wir brauchen mehr Kerne“ denkt, hat meist die falsche Spur.
Die CPU-Zeit selbst hat zwei Gesichter, die man auseinanderhalten muss. Das eine ist die echte Rechenlast: Eine Abfrage verarbeitet tatsächlich riesige Datenmengen, sortiert, aggregiert, vergleicht — und verbrennt dabei legitim Rechenzeit, nur eben viel zu viel davon, weil ihr Plan ineffizient ist. Das andere ist die Parallelitäts-Last: Eine Abfrage wird über viele Kerne verteilt, und ein großer Teil der „CPU-Zeit“ geht für die Koordination der parallelen Teile drauf, sichtbar als CXPACKET-Wartetyp. Beide sehen im Task-Manager gleich aus — hundert Prozent —, verlangen aber unterschiedliche Antworten. Genau deshalb diagnostiziert man in Schichten, statt zu raten.
Diese Schichten sind die Diagnose-Pyramide. Ganz oben, am schnellsten zu beantworten, steht die Frage nach der Session: Wer verbraucht gerade die meiste CPU? Die nächste Schicht ist der Wartetyp: Worauf wartet dieser Verbraucher — auf echte Rechenzeit (SOS_SCHEDULER_YIELD) oder auf parallele Koordination (CXPACKET)? Die Basis ist der Ausführungsplan: Warum ist diese Abfrage so teuer — fehlt ein Index, scannt sie statt zu suchen, hat sie einen schlechten Plan durch Parameter Sniffing? Von oben nach unten wird die Diagnose präziser, und erst die Basis sagt dir, was zu tun ist. Ein Gedankenexperiment macht den Sinn der Reihenfolge deutlich: Stell dir vor, du springst gleich zur Basis und siehst einen teuren Tabellen-Scan im Plan. Reflexartig legst du einen Index an — und stellst fest, dass die CPU weiter brennt, weil der eigentliche Verbraucher eine ganz andere Session war, die du nie angeschaut hast. Oder umgekehrt: Du springst sofort auf den höchsten Wartetyp, siehst CXPACKET und schaltest Parallelität ab, obwohl die eine teure Abfrage mit einem Index in Millisekunden fertig gewesen wäre. Die Pyramide schützt vor genau diesen Fehlschlüssen, indem sie erst klärt, wer überhaupt das Problem ist, bevor sie fragt, warum.
|
Die zwei Gesichter der CPU-Last Echte Rechenlast — die Abfrage verarbeitet wirklich zu viel (schlechter Plan, fehlender Index). Wartetyp oft SOS_SCHEDULER_YIELD. Lösung: den Plan reparieren. |
|---|
Die Diagnose-Pyramide
Im Notfall arbeitest du die Pyramide von oben nach unten ab — Session, Wait, Plan — und triffst erst danach eine Entscheidung zwischen Quick-Win und langfristiger Lösung. Die folgende Skizze zeigt den ganzen Weg:

Die Skripte: den Brandstifter finden
Schritt 1 — Wer frisst die CPU gerade?
Die Spitze der Pyramide: die aktiven Anfragen, nach CPU-Zeit sortiert. Die obersten Zeilen sind die Hauptverdächtigen im Moment:
|
— Top-CPU-Verbraucher unter den aktiven Anfragen SELECT r.session_id, r.cpu_time, r.total_elapsed_time, r.wait_type, r.command, 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 AktuellesStatement FROM sys.dm_exec_requests AS r CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) AS st WHERE r.session_id > 50 ORDER BY r.cpu_time DESC; |
|---|
Schritt 2 — Die teuersten Abfragen über die Zeit
Der Momentwert verrät nicht immer den Dauer-Brandstifter. Diese Abfrage zeigt die kumuliert teuersten Abfragen seit dem letzten Neustart — die Wiederholungstäter:
|
— Die CPU-hungrigsten Abfragen seit dem letzten Neustart SELECT TOP (15) qs.total_worker_time / 1000 AS GesamtCPU_ms, qs.execution_count AS Ausfuehrungen, qs.total_worker_time / qs.execution_count / 1000 AS AvgCPU_ms, SUBSTRING(st.text, (qs.statement_start_offset/2)+1, 200) AS Auszug 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; |
|---|
Achte auf die Kombination aus AvgCPU_ms und Ausfuehrungen: Eine Abfrage mit moderater Durchschnitts-CPU, die aber zehntausendfach läuft, kann mehr Schaden anrichten als der einmalige Riesen-Report. Beide Sorten von Brandstiftern findest du hier.
Schritt 3 — Wait Stats: echte Last oder Parallelität?
|
— Aktuelle Wartetypen der CPU-aktiven Sessions einordnen SELECT wait_type, COUNT(*) AS Anzahl, SUM(wait_duration_ms) AS SummeWarteMs FROM sys.dm_os_waiting_tasks WHERE session_id > 50 GROUP BY wait_type ORDER BY SummeWarteMs DESC;
— CXPACKET dominiert -> Parallelitaet: Cost Threshold pruefen — SOS_SCHEDULER_YIELD -> echte CPU-Last: Plan/Index reparieren |
|---|
Die sieben häufigsten Brandstifter
Eine CPU am Anschlag hat fast immer einen dieser sieben Gründe. Für die tieferliegenden hat der Notarztwagen jeweils einen eigenen Detail-Beitrag:
1. Die entartete Einzel-Abfrage
Der Klassiker: Eine einzige Abfrage scannt riesige Tabellen, weil ein Index fehlt oder ihr Plan schlecht ist, und verbrennt im Alleingang die ganze CPU. Über die Skripte oben gefunden, oft mit einer einzelnen Index-Anlage oder Plan-Korrektur entschärft.
2. Parameter Sniffing
Ein Plan, der für einen Sonderfall optimiert wurde, wird auf den Normalfall losgelassen — dieselbe Abfrage läuft mal schnell, mal katastrophal. Akut hilft OPTION (RECOMPILE) oder Plan-Forcing; die Mechanik steht im Statistiken-Beitrag (/sql-server-statistiken-veraltet/).
3. Fehlende Indizes
Ohne den passenden Index bleibt dem Server nur der Tabellen-Scan — viel CPU für wenig Ergebnis. Die Missing-Index-DMVs zeigen Kandidaten, aber mit Bedacht: nicht jeden Vorschlag blind umsetzen. Mehr zur Strategie im Index-Wartungs-Beitrag (/sql-server-index-wartung/).
4. Veraltete Statistiken
Plant der Optimizer auf veralteten Mengen-Schätzungen, wählt er teure Pläne — etwa einen Scan, wo ein Seek genügt hätte. Ein gezieltes Statistik-Update kann die CPU schlagartig entlasten. Details im Statistiken-Beitrag.
5. Übermäßige Parallelität (CXPACKET)
Zu billige Abfragen werden auf alle Kerne verteilt, der Koordinations-Overhead frisst CPU. Die Lösung ist fast immer der höhere Cost Threshold for Parallelism (Default 5 ist zu niedrig), nicht das pauschale Abschalten der Parallelität.
6. Plan-Cache-Probleme und Recompiles
Ständiges Neukompilieren von Plänen (etwa durch ungünstiges Caching oder massenhaft Ad-hoc-Abfragen) kostet selbst CPU. Hier hilft oft die Server-Einstellung „Optimize for Ad hoc Workloads“ und parametrisierter Code statt String-Konkatenation.
7. Echte Überlast
Der seltenste Fall, aber er existiert: Zu viele gleichzeitige, an sich legitime Abfragen für die vorhandene Hardware. Erst wenn die anderen sechs ausgeschlossen sind, ist das ein Thema — und dann oft eher eine Frage der Lastverteilung als der reinen Kernzahl. In diesem seltenen Fall lohnt die Überlegung, ob die Last sich verschieben oder aufteilen lässt: schwere Auswertungs-Abfragen in ein Wartungsfenster oder auf ein lesbares Always-On-Secondary verlagern, statt sie tagsüber mit dem OLTP-Betrieb um dieselben Kerne konkurrieren zu lassen. Mehr Hardware ist dann immer noch eine Option — aber eine, die man bewusst und als letzte wählt, nachdem die Effizienz-Hebel ausgereizt sind, nicht als ersten Reflex, der das eigentliche Problem nur überdeckt.
Was du auf keinen Fall tun solltest
|
Die drei häufigsten Anfängerfehler 1. MAXDOP gleich 1 als Allheilmittel — und damit große Abfragen ausbremsen, statt die Ursache zu finden. |
|---|
Nachsorge: damit es nicht wiederkommt
Ist die akute Last gelöscht, beginnt die eigentliche Arbeit — denn die entartete Abfrage kommt wieder, wenn ihre Ursache bleibt. Drei Schritte für die Nachsorge: Erstens, die Konfiguration in Ordnung bringen — Cost Threshold for Parallelism auf einen zeitgemäßen Wert (oft 50 als Start) und MAXDOP bewusst an die Hardware angepasst; allein das nimmt viel unnötige CXPACKET-Last. Zweitens, die Index- und Statistik-Strategie angehen, damit der Optimizer gute Pläne wählen kann — die häufigste strukturelle Ursache teurer Abfragen. Drittens, den Query Store als Frühwarnsystem aktivieren: Er zeigt die teuersten Abfragen über die Zeit und macht Plan-Regressionen sichtbar, bevor sie zum Notfall werden — und erlaubt im Ernstfall das sofortige Plan-Forcing. Der akute Griff löscht das Feuer; die Nachsorge entfernt das Streichholz.
Vertiefung in der Buchreihe
CPU-Diagnose, Parallelität und die Optimizer-Mechanik füllen in der Buchreihe „SQL Server in der Praxis“ eigene Kapitel — alle Bände in der Übersicht auf der SQL-Pillar-Seite unter /sql-server/.
|
Empfohlene Bände Band 1: Performance & Troubleshooting — das Heimatbuch dieses Beitrags: CPU-Diagnose, Wait Stats, Parallelität (MAXDOP, Cost Threshold), Plan-Analyse und die Index-Strategie. |
|---|
Verwandte Notfälle im Notarztwagen
Eine brennende CPU hat enge Nachbarn — diese Beiträge sind die nächste Station:
Drei Wege, wenn die CPU jetzt gerade brennt
Akut-Hotline
Wenn die CPU in diesem Moment am Anschlag steht und der Betrieb steht: anrufen. Halte bereit, ob ein einzelner Verursacher erkennbar ist (eine Session, die die cpu_time-Liste anführt) und welcher Wartetyp dominiert — CXPACKET oder SOS_SCHEDULER_YIELD. Das sind die zwei Informationen, die den schnellsten Weg bestimmen. Meist lässt sich gemeinsam in Minuten klären, ob es eine entartete Abfrage, ein Sniffing-Problem oder zu niedriger Cost Threshold ist — und gezielt entschärfen, statt mit MAXDOP-1 alles auszubremsen.
Festpreis-Analyse
Wenn die CPU immer wieder hochläuft und du die strukturelle Ursache abstellen willst? Die Festpreis-Analyse liefert sie: Ich identifiziere die teuersten Abfragen über die Zeit, prüfe Index- und Statistik-Strategie, kontrolliere die Parallelitäts-Konfiguration (MAXDOP, Cost Threshold) und liefere einen Bericht mit priorisierten Maßnahmen, von der konkreten Index-Empfehlung bis zur Konfigurations-Korrektur. Zum vereinbarten Festpreis, ohne Stundenzähler.
Selbst-Diagnose-Kit
Für alle, die den nächsten CPU-Brand selbst löschen wollen: Das Diagnose-Kit bündelt die Skripte aus diesem Beitrag — Top-CPU-Session-Abfrage, die teuersten Abfragen über die Zeit, die Wait-Stats-Einordnung — plus die Diagnose-Pyramide zum Ausdrucken und eine Konfigurations-Checkliste für MAXDOP und Cost Threshold. Damit findest du den Brandstifter in Minuten, statt blind am Thermostat zu drehen.
|
Ein letzter Tipp Prüfe heute, solange die CPU ruhig ist, eine einzige Einstellung: Steht der Cost Threshold for Parallelism noch auf dem Standardwert 5? Eine Abfrage, dreißig Sekunden. Wenn ja, hast du gerade einen der lohnendsten und risikoärmsten Hebel gegen CPU-Last gefunden — ein zeitgemäßer Wert (oft 50 als Start) nimmt schon im Ruhezustand viel unnötige Parallelität heraus, bevor sie das nächste Mal zum roten Vollausschlag wird. Das ist Vorsorge, die fünf Minuten kostet und einen ganzen Notfall ersparen kann. |
|---|
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 die SQL-Server-CPU bei 100 Prozent?
Fast immer, weil eine oder wenige Abfragen weit mehr Rechenarbeit verlangen, als sie sollten — meist wegen eines schlechten Ausführungsplans. Die typischen Brandstifter: eine entartete Abfrage, die statt eines gezielten Index-Seeks die ganze Tabelle scannt; Parameter Sniffing, bei dem ein für einen Sonderfall optimierter Plan auf den Normalfall losgelassen wird; fehlende Indizes; veraltete Statistiken; oder übermäßige Parallelität, bei der zu billige Abfragen auf alle Kerne verteilt werden. Seltener ist es echte Überlast durch zu viele gleichzeitige Anfragen für die vorhandene Hardware. Die Diagnose folgt einer Pyramide: erst wer die CPU frisst, dann worauf er wartet, dann warum sein Plan so teuer ist. Der ganze Pfad steht in diesem Beitrag.
Welche Query verursacht die hohe CPU-Last?
Das findest du über sys.dm_exec_requests, nach cpu_time absteigend sortiert — die obersten Sessions sind die Hauptverbraucher im Moment. Für das größere Bild über die Zeit liefert sys.dm_exec_query_stats die teuersten Abfragen seit dem letzten Neustart (kumulierte und durchschnittliche CPU-Zeit), und der Query Store zeigt dieselbe Information persistent, also auch über Neustarts hinweg. Sobald du die Abfrage hast, holst du dir ihren Ausführungsplan und suchst die teure Operation: ein Scan statt eines Seeks, eine fehlende Index-Nutzung, eine ungünstige Join-Reihenfolge. Die konkreten Abfragen dafür stehen im Skript-Abschnitt dieses Beitrags.
Was bedeutet der Wartetyp CXPACKET?
CXPACKET steht für die Koordination paralleler Abfrageteile — er taucht auf, wenn eine Abfrage über mehrere CPU-Kerne parallel ausgeführt wird und die Teile aufeinander warten. Wichtig: CXPACKET ist kein Fehler und kein Feind, sondern oft nur ein Hinweis. Viele CXPACKET-Waits bedeuten meist nicht, dass Parallelität an sich schlecht ist, sondern dass zu billige Abfragen parallelisiert werden, die seriell schneller liefen. Die richtige Antwort ist deshalb fast nie, Parallelität ganz abzuschalten (MAXDOP auf 1), sondern den Cost Threshold for Parallelism hochzusetzen, ab dem eine Abfrage überhaupt parallel gehen darf — der Standardwert von 5 ist auf moderner Hardware viel zu niedrig.
Wie setze ich Cost Threshold for Parallelism richtig?
Der Cost Threshold for Parallelism bestimmt, ab welchen geschätzten Plankosten der SQL Server eine Abfrage überhaupt parallel ausführt. Der eingebaute Standardwert ist 5 — eine Zahl aus den 1990er-Jahren, die auf moderner Hardware viel zu niedrig ist und dazu führt, dass schon triviale Abfragen parallelisiert werden, was mehr Koordinations-Overhead als Nutzen bringt. Ein verbreiteter, sinnvoller Startwert ist 50, von dem aus man je nach Workload nachjustiert. Das Hochsetzen ist eine der wirkungsvollsten und risikoärmsten Konfigurationsänderungen gegen unnötige CXPACKET-Last — und fast immer die bessere Antwort als der pauschale Griff zu MAXDOP gleich 1.
Welcher MAXDOP-Wert ist der richtige?
MAXDOP (Maximum Degree of Parallelism) begrenzt, über wie viele Kerne eine einzelne Abfrage maximal parallel laufen darf. Der richtige Wert hängt von der Hardware ab: Microsofts Empfehlung orientiert sich an der Zahl der Kerne pro NUMA-Knoten und liegt bei modernen Servern oft bei 8 oder dem Wert eines NUMA-Knotens, je nachdem, was kleiner ist. Wichtig ist, was MAXDOP NICHT sein sollte: pauschal 1. Das nimmt großen, analytischen Abfragen jede Parallelität und macht sie oft drastisch langsamer — es löst ein CXPACKET-Symptom, indem es ein größeres Performance-Problem schafft. MAXDOP gehört bewusst an die Hardware angepasst, nicht aus Verzweiflung auf 1 gestellt.
Wie analysiere ich Parameter Sniffing?
Parameter Sniffing heißt: Der SQL Server erstellt beim ersten Aufruf einer parametrisierten Abfrage einen Plan, der auf die damals übergebenen Parameterwerte optimiert ist — und verwendet diesen Plan dann für alle weiteren Aufrufe, auch wenn deren Werte ganz andere Datenmengen betreffen. War der erste Aufruf ein Sonderfall (etwa ein seltener Wert mit wenigen Treffern), kann der gespeicherte Plan für den Normalfall katastrophal sein. Erkennen lässt es sich daran, dass dieselbe Abfrage mal blitzschnell und mal extrem langsam läuft, ohne dass sich die Daten groß ändern. Der Query Store macht das über die Plan-Historie sichtbar. Akute Abhilfe schafft OPTION (RECOMPILE) oder Plan-Forcing — Details im Statistiken-Beitrag.
Wie aktiviere ich den Query Store?
Pro Datenbank mit ALTER DATABASE [DB] SET QUERY_STORE = ON, gefolgt von den Sammeleinstellungen (Modus READ_WRITE, Größe, Aufbewahrung). Im Kontext hoher CPU-Last ist der Query Store doppelt wertvoll: Er zeigt zuverlässig die teuersten Abfragen nach CPU über einen wählbaren Zeitraum (statt nur im Moment), und er macht Plan-Regressionen sichtbar — also den Fall, dass eine Abfrage nach einem Plan-Wechsel plötzlich viel mehr CPU verbraucht. Mit dem Plan-Forcing kannst du dann den bewährten alten Plan erzwingen und die Last sofort entschärfen. Der Query Store gehört auf jede Produktions-Datenbank, idealerweise aktiviert, bevor das CPU-Problem auftritt — der eigene Beitrag erklärt ihn ausführlich.
