SQL Performance-Analyse (hypothetisches Beispiel)

von | Sep. 23, 2025 | Fachartikel, SQL Server | 0 Kommentare

Consulting, Beratung

SQL Performance-Analyse (hypothetisches Beispiel)

Management Summary

Die Performance-Analyse einer Microsoft SQL-Server-Instanz (Version 2019) hat CPU- und I/O-Engpässe als Hauptprobleme identifiziert. In Spitzenzeiten lag die CPU-Auslastung dauerhaft über 90 %, und die Speicher-I/O-Latenz der Datenbanken überschritt zeitweise 20 ms – beides Indikatoren für ausgelastete Ressourcen. Als Ursachen wurden vor allem ineffiziente Abfragepläne ausgemacht: Insbesondere fehlende Indizes führten zu teuren Volltabellenscans und unnötig hoher CPU- sowie I/O-Belastung. Zudem war die Parallelität suboptimal eingestellt (niedriger Cost Threshold, Standard-MAXDOP), was zu übermäßigen CXPACKET-Wartezeiten (~35 % Anteil) und zusätzlichem CPU-Overhead führte.

Empfohlene Maßnahmen mit hoher Wirkung sind unter anderem: das Anlegen eines gezielten Nonclustered Index für kritische Abfragen (reduziert Vollscans), die Anpassung der Parallelisierungs-Einstellungen (z.B. Anheben des Cost Threshold for Parallelism und Überprüfung von MAXDOP) sowie Optimierungen an TempDB– und Transaktionslog-Konfiguration (ausreichende Anzahl an TempDB-Dateien, Vorab-Größenzuweisung fürs Log). Diese Schritte versprechen deutliche Verbesserungen in Durchsatz und Reaktionszeit bei geringem Risiko. Nebenwirkungen der Änderungen – etwa ein leicht erhöhter Speicherbedarf durch neue Indizes oder längere Laufzeiten einzelner Queries bei reduzierter Parallelität – sind überschaubar. Eine engmaschige Überwachung der CPU-Wartezeiten (Signal Waits), I/O-Latenzen und dominanten Wartetypen wird empfohlen, um die Wirksamkeit der Maßnahmen zu validieren und eventuelle neue Flaschenhälse frühzeitig zu erkennen.

Methodik der Analyse (Vorgehensweise)

Eine strukturierte Performance-Analyse folgt einem klaren Vorgehensmodell. In diesem Beispiel wurde wie folgt vorgegangen:

  1. Problemdefinition präzisieren: Zunächst wurde der Scope des Problems abgegrenzt – betroffene Zeiträume (hier vor allem die Peak-Zeiten zwischen 09:00–11:00 Uhr), involvierte Datenbanken/Abfragen und die beobachteten Symptome (z.B. zeitweise hohe Latenzen und Query-Timeouts) wurden definiert. So war klar, wann und wo die Performance-Probleme auftreten.
  2. Workload-Charakterisierung: Als Nächstes wurde die Art der Arbeitslast eingeschätzt. Handelt es sich um OLTP-Workload (viele kurze Transaktionen), um Reporting/Analysen, einen Mixed Workload oder ETL-Batchfenster? Im Beispiel zeigte sich ein gemischtes Muster: Primär OLTP am Tag mit einzelnen schweren Reporting-Queries am Morgen. Parallelität und Batch-Größen wurden hierbei beachtet – es gab viele gleichzeitige kleine Anfragen und einige periodische Batch-Jobs.
  3. Ressourcenebenen prüfen (Top-Down): Die Analyse erfolgte schrittweise über die Ressourcen: zuerst CPU, dann Speicher, dann I/O/Netzwerk. Darauf aufbauend wurde das SQL Server-internes Wartesystem (Wait Stats) betrachtet und schließlich spezifische Datenbankobjekte und Abfragepläne geprüft. Dieser Drilldown von Hardware zu SQL-Interna isoliert Engpässe systematisch.
  4. Korrelation zeitlicher Abläufe: Die zeitlichen Verläufe der gemessenen Performance-Counter (CPU-Auslastung, I/O-Latenzen, etc.) wurden mit Ereignissen korreliert. So wurden z.B. Wartezeiten, Blockierungen oder geplante Jobs (Backups um 02:00 Uhr, Index-Maintenance) in Beziehung zu Peaks in den System-Countern gesetzt. Im Ergebnis ließ sich erkennen, dass z.B. ein Backup-Lauf um 02:00 Uhr erwartungsgemäß einen temporären Anstieg der I/O-Latenz und einen Abfall der Pufferverfügbarkeit (PLE) verursachte, während die Latenzspitzen am Vormittag nicht durch Wartung, sondern durch Benutzer-Workload entstanden.
  5. Bottleneck isolieren: Mit den obigen Schritten konnten die Haupt-Engpässe herausgearbeitet werden. Identifiziert wurden die Top-Wartetypen und ressourcenhungrigsten Abfragen, sowie betroffene Dateien/Dateigruppen und Indizes. In diesem Beispiel kristallisierte sich die CPU als limitierender Faktor heraus, im Zusammenspiel mit intensiver I/O. Ein bestimmtes Datenbankobjekt (große Tabelle ohne passenden Index) und suboptimale Abfragepläne standen im Zentrum der Probleme.
  6. Hypothesen testen: Basierend auf den Erkenntnissen wurden Hypothesen formuliert – z.B. „Ein neuer Index auf Tabelle X reduziert die Scanlast und damit CPU+I/O-Verbrauch“. Diese Hypothesen sollten idealerweise verifiziert werden, etwa durch gezielte Messungen vor und nach einer Änderung oder A/B-Vergleiche (z.B. einmal Ausführen der Abfrage mit Index und einmal ohne). In unserem Fall wurden Änderungen zunächst in einer Testumgebung geprüft, um die Auswirkung auf Laufzeit und Ressourcenverbrauch zu bestätigen, bevor sie produktiv umgesetzt wurden.
  7. Abschluss & Maßnahmenplan: Abschließend wurde ein Maßnahmenplan erstellt mit konkreten Schritten, Verantwortlichkeiten und Prioritäten (siehe unten). Zudem wurde eine Monitoring-Baseline definiert, um nach Implementierung der Maßnahmen den Erfolg zu messen. Ein Rollback-Plan für kritische Änderungen (z.B. Konfigurationsanpassungen) wurde ebenfalls vorbereitet, um Risiken zu minimieren.

Dieses methodische Vorgehen gewährleistet eine nachvollziehbare Analyse, bei der alle Ebenen – vom Betriebssystem bis zur Abfrage – berücksichtigt und die Maßnahmen durch Messungen abgesichert werden.

Detailanalyse der Messpunkte und Ergebnisse

In der Detailanalyse wurden die relevanten Metriken und Messpunkte der SQL-Server-Instanz ausgewertet. Die wichtigsten Kategorien (CPU, Speicher, I/O, TempDB, Wait-Statistiken, Abfrage-Ebene, Sperren, Indizes, Transaktionslog, Verfügbarkeit, Virtualisierung) werden im Folgenden jeweils mit Befunden des Beispiels und deren Interpretation dargestellt. (Hinweis: Da keine echten Produktionsdaten vorlagen, werden im Folgenden Beispieldaten verwendet, die jedoch realistische Größenordnungen widerspiegeln. Schwellwerte sind als Richtwerte zu verstehen, abhängig von Workload und Umgebung.)

CPU-Auslastung

Beobachtungen: Die CPU-Auslastung des Servers (PerfMon Counter % Processor Time) lag über den betrachteten Zeitraum hinweg hoch, mit Spitzen von 90–95 % während der Geschäftszeit. Auch der Prozess-spezifische Wert Process(sqlservr)\% Processor Time bestätigte, dass hauptsächlich der SQL-Server-Prozess die CPU beanspruchte. Die Rate der eingehenden Anfragen (Batch Requests/sec) pendelte um 300–500 pro Sekunde und erreichte zur Spitzenlast ~600/sec.

Interpretation: Ein dauerhaft hoher CPU-Wert über 80 % deutet auf einen CPU-Bottleneck oder ineffiziente Ausführungspläne hin. In unserem Beispiel war die CPU tatsächlich nahezu ausgelastet, insbesondere vormittags. Auffällig ist, dass die Batch-Request-Rate zwar hoch, aber nicht extrem (z.B. kein vierstelliger Wert) war – die CPU-Last stand also nicht nur durch schiere Menge an Queries, sondern durch teure Einzelabfragen im Zusammenhang. Dies deutet darauf hin, dass einige Abfragen überproportional viel CPU verbrauchen (z.B. durch Scans großer Tabellen oder komplexe Joins), anstatt dass die CPU nur wegen sehr vieler trivialer Abfragen voll ausgelastet ist. Wäre die CPU niedrig geblieben trotz hoher Anfragerate, hätte dies eher auf einen anderen Flaschenhals (I/O oder Locking) hingedeutet – hier jedoch korreliert die hohe CPU mit mäßiger Durchsatzrate, was ein CPU-bound-Szenario nahelegt.

Zusätzlich zeigte eine Auswertung der CPU-Scheduler (DMV sys.dm_os_schedulers) während der Peaks, dass alle Scheduler ausgelastet waren – es gab regelmäßig wartende Tasks (runnable tasks), d.h. Threads, die auf freie CPU-Zeit warteten. Dies wird untermauert durch einen erhöhten Signal Wait Time Anteil (siehe Wait-Statistiken unten). Zusammenfassend war die Instanz im fraglichen Zeitraum CPU-limitiert, was prioritäres Handeln in diesem Bereich erforderlich macht.

Speicher (RAM) und Pufferpool

Beobachtungen: Der SQL Server verfügte über 32 GB zugewiesenen RAM. Davon wurde nahezu der volle Betrag als Target Server Memory erreicht (Total Server Memory ~30 GB, also der SQL-Bufferpool nutzte fast alles Zugewiesene). Der Page Life Expectancy (PLE) – also die Verweildauer von Seiten im Speicher – lag im Normalbetrieb bei ~6000 Sekunden, fiel jedoch während intensiver Vorgänge mehrfach drastisch ab (z.B. auf 500–800 s). Im Monitoring-Graph ergab sich ein typisches Sägezahn-Muster: nach längerer ruhiger Phase sorgte eine große Abfrage oder ein Backup dafür, dass viele Seiten aus dem Puffer verdrängt wurden, woraufhin PLE stark sank und sich danach langsam wieder erholte. Die Buffer Cache Hit Ratio blieb insgesamt hoch bei über 99 %, sank aber während jener Phasen kurzfristig ab, was zeigt, dass normalerweise fast alle Lesezugriffe aus dem RAM bedient werden, außer wenn ungewöhnlich viele neue Seiten eingelesen werden müssen. Der PerfMon Counter Memory Grants Pending war die meiste Zeit 0, stieg aber in Einzelfällen (z.B. bei einer großen Reporting-Abfrage morgens gegen 09:15 Uhr) kurzzeitig auf 1–2 an.

Interpretation: Insgesamt deutet dies auf eine knappe, aber noch ausreichende Speicherausstattung hin. Ein dauerhaft sehr niedriger PLE (z.B. <300 s) oder kontinuierlich ansteigende Memory Grants Pending wären Anzeichen für Speicherengpässe. Hier waren solche Werte nur episodisch: Die Sägezahn-Abfälle des PLE synchron zu bestimmten Lastspitzen deuten auf große Scans oder Backups hin, die viele Seiten neu einlesen (typischerweise sinkt PLE beim Full-Backup oder massiven Index-Scans). Nach Abschluss erholt sich PLE wieder – das heißt, es gibt keinen permanenten Speichermangel, sondern temporäre Lastspitzen im Speicher. Die Buffer Hit Ratio nahe 100 % im Normalfall ist gut (fast alle Anfragen finden ihre Daten im Cache), leichte Einbrüche während großer Lesevorgänge sind normal. Memory Grants Pending > 0 bedeutet, dass Abfragen auf Arbeitsspeicher für Sortier-/Join-Operationen warten mussten. In unserem Beispiel geschah dies selten und kurz – ein Hinweis, dass gelegentlich eine Abfrage mehr Speicher für Sortierung oder Hash-Joins wollte, als sofort frei war. Insgesamt war der Speicher aber nicht der primäre Flaschenhals; eher zeigt sich, dass große Abfragen den vorhandenen RAM ausreizen und dadurch temporär Puffer verdrängen. Maßnahmen wie mehr RAM oder optimierte Abfragen (z.B. Vermeidung unnötig großer Sorts durch Indexe) könnten hier helfen, wobei der Hauptfokus auf CPU/I/O liegen sollte, da Speicherengpässe nicht dauerhaft waren.

I/O und Storage-Leistung

Beobachtungen: Für die I/O-Analyse wurden sowohl Betriebssystem-Counter (z.B. Avg. Disk sec/Read, Disk Transfers/sec) als auch SQL-Server-spezifische DMVs (sys.dm_io_virtual_file_stats) ausgewertet. Der Server nutzt getrennte Laufwerke für Daten (D:-Laufwerk) und Transaktionslog (L:-Laufwerk), sowie ein eigenes Laufwerk für TempDB. Während normaler Last lagen die Lese-Latenzen der Datenträger im Bereich von 10–15 ms und die Schreib-Latenzen bei 5–10 ms. Unter Spitzenlast (durch die großen Scans am Vormittag) stiegen die durchschnittlichen Read-Latenzen zeitweise auf 25–30 ms an. Die IOPS (I/O-Operationen pro Sekunde) für das Datenlaufwerk lagen bei ~200–300 im Durchschnitt und gingen in Lastphasen auf 500+ hoch, während das Log-Laufwerk konstant ~100 IOPS mit kleineren Peaks aufwies. Die folgende Tabelle zeigt eine Beispiel-Auswertung der I/O-Latenzen pro Datei aus der DMV sys.dm_io_virtual_file_stats (Werte aggregiert über den Analysezeitraum):

Datenbank

Datei

Lese-Latenz (ms)

Schreib-Latenz (ms)

IOPS

Durchsatz (MB/s)

Bemerkung

SalesDB

SalesDB.mdf (Data)

18,0

5,2

250

12,5

Haupt-DB, moderate Leselast

SalesDB

SalesDB_Log.ldf (Log)

1,4

8,0

120

8,8

Log-Datei, Schreiben ok

TempDB

tempdb.mdf (1 von 8 Dateien)

12,5

3,8

80

5,0

TempDB, verteilte Last

(Beispieldaten: Latency in Millisekunden; IOPS: gemittelte I/O-Operationen pro Sekunde; Durchsatz angenähert aus IOPS * 8 KB.)

Interpretation: Im Normalbetrieb sind Latenzen unter 10 ms sehr gut, 10–20 ms noch akzeptabel. In unserem Beispiel waren die Grundwerte im grünen Bereich, jedoch zeigen 25–30 ms Read-Latenz in Peakzeiten eine kritische Verzögerung für OLTP-Verhältnisse. Die Datenbank SalesDB (fiktiver Haupt-Workload) musste bei großen Leseoperationen bis zu 25 ms auf Platten-I/O warten – dieser Wert liegt über dem üblichen Schwellenwert von ~20 ms, was sich spürbar auf Abfrage-Laufzeiten auswirken kann. Die Schreib-Latenz des Log-Laufwerks (~8 ms Spitze) war hingegen im akzeptablen Rahmen (typisch <10 ms sind in Ordnung für Logs).

Der Durchsatz und die IOPS-Zahlen deuten darauf hin, dass insbesondere beim Lesen relativ viele Daten bewegt wurden (z.B. ~12 MB/s an Lesen für SalesDB während der Messung). Kombiniert mit dem Wissen um Vollscans (siehe Abfrage-Analyse) wird deutlich, dass die I/O-Last primär durch große Lesevorgänge getrieben wurde. Die TempDB-Dateien (hier exemplarisch eine von 8 gezeigt) wiesen moderate Latenz (~12 ms) auf; es gab also keine extreme TempDB-I/O-Bremse, was auch daran liegt, dass sich die Last auf 8 Dateien verteilt hat. Insgesamt zeigt die I/O-Analyse: Storage trug zum Gesamt-Bottleneck bei, insbesondere indem es die großen Lesezugriffe nicht schneller bedienen konnte. Zwar ist die gemessene Lese-Latenz nicht exorbitant hoch, aber kombiniert mit dem hohen Datenvolumen führte sie zu merklichen Wartezeiten auf I/O** (siehe Wait Stats). Verbesserungen auf I/O-Ebene (z.B. schnellere Platten oder Reduktion der zu lesenden Datenmenge durch Indizierung) würden direkt der Performance zugutekommen.

TempDB und temporäre Objekte

Beobachtungen: Die TempDB-Konfiguration bestand aus 8 gleich großen Datenfiles (je 10 GB) plus 1 Logfile, was für den 8-Kern-Server eine passende Anzahl ist (Faustregel: 1 TempDB-Datei pro CPU-Kern bis max. ~8). Einstellungen wie Mixed Extent Allocation sind in neueren SQL-Server-Versionen standardmäßig optimiert (Trace Flags 1117/1118 sind ab 2016 obsolet, da standardmäßig uniform extent allocation aktiv ist). Während der Analyse wurde die Auslastung der TempDB überwacht: es fielen einige PAGELATCH_* Waits auf, konkret auf PFS-Seiten, aber mit geringem Anteil (siehe Wait-Statistiken). Es gab keine Anzeichen für gravierende Contention (z.B. SGAM/PFS-Latches, die sehr hohe Wartezeiten verursachen). Die TempDB-I/O war ebenfalls relativ unauffällig (siehe I/O-Tabelle, moderate Latenzen).

Interpretation: Eine gut konfigurierte TempDB ist entscheidend, da viele parallele Operationen (Temp Tabellen, Sortierungs-Spills, Version Store bei Snapshot Isolation etc.) sonst zu Engpässen führen können. In unserem Beispiel scheinen bereits Best Practices umgesetzt: 8 Dateien sorgten für eine Verteilung der Allokationen. Die beobachteten leichten PFS-Latches (< 5 % der Gesamtwartezeit) deuten zwar auf parallele TempDB-Nutzung hin, waren aber nicht dominant. Hätten wir hier sehr hohe PAGELATCH_UP/PAGELATCH_EX-Werte gesehen, wäre die Maßnahme, weitere TempDB-Dateien hinzuzufügen oder Größenunterschiede zu beseitigen, angebracht. Auch historische Empfehlungen wie Traceflag 1117/1118 spielen hier keine Rolle mehr, da die Instanz (2019) bereits uniform allocation nutzt. Zusammenfassend war TempDB kein Haupt-Engpass in diesem Szenario – die Konfiguration ist angemessen, und es gab keine auffälligen Blockierungen durch TempDB-Latches. Natürlich bleibt zu überwachen, ob z.B. Version Store-Wachstum (bei Verwendung von RCSI/Snapshot Isolation) oder Temp-Datei-Auslastung langfristig unproblematisch sind, aber im Untersuchungszeitraum gab es keinen akuten Handlungsbedarf in diesem Bereich.

Wait-Statistiken (Wartearten)

Beobachtungen: Die kumulierten Wait-Statistiken der SQL-Server-Instanz wurden seit dem letzten Restart ausgewertet, um zu sehen, welche Wartetypen (Wait Types) am meisten Zeit in Anspruch nahmen. Die folgende Tabelle zeigt die Top-Waits über den betrachteten Zeitraum (fiktive Darstellung):

Wartetyp

Gesamt Wartezeit (ms)

Anteil (%)

Interpretation (Deutung)

Nächste Schritte

CXPACKET

120.000

35 %

Wartezeit durch parallele Abfragen (Thread-Synchronisation); deutet auf suboptimale Parallelisierungsstrategien oder ungleiche Workloadverteilung hin.

Parallelismus prüfen: z.B. MAXDOP und Cost Threshold anpassen, Query-Pläne auf DOP-Skew untersuchen.

PAGEIOLATCH_SH

80.000

23 %

Warten auf das Laden von Seiten (shared) von Disk in den Speicher; spricht für I/O-Bottleneck bei Lesezugriffen (z.B. große Scans, langsamer Storage).

I/O verbessern: Index anlegen um Scans zu vermeiden, ggf. schnellere Storage verwenden, mehr RAM falls Cache zu klein.

WRITELOG

50.000

15 %

Warten auf Log-Schreibvorgänge (Commit-Latenz); Indikator für Log-I/O-Engpass oder sehr viele Transaktionen, die auf Bestätigung warten.

Log-Optimierung: schnellere Log-Disk, größere Log-Datei um Autogrowth zu vermeiden, Transaktionsbündelung prüfen.

SOS_SCHEDULER_YIELD

25.000

7 %

Threads geben freiwillig die CPU ab (Zeitquantum aufgebraucht); bei hoher Häufigkeit ein Zeichen für CPU-Druck – Tasks müssen auf CPU-Zuteilung warten.

CPU-Engpass adressieren: Abfragen optimieren (geringere CPU-Last), ggf. CPU-Ressourcen erhöhen, Signal-Waits beobachten.

PAGELATCH_UP

30.000

9 %

Warten auf In-Memory-Latch (Update) – oft bei TempDB-Allocation oder Bufferpool-Seiten; hier moderat, kann aber auf TempDB-Engpässe hindeuten.

Falls hoch: mehr TempDB-Dateien, Verteilung prüfen. In unserem Fall nur moderat – weiter beobachten.

LCK_M_X (Locks)

20.000

6 %

Warten auf exklusive Sperren – zeigt Blockierungen durch lange Transaktionen oder fehlende Indizes (wenn Schreiboperationen auf freigegebene Ressourcen warten).

Lock-Analyse: lange Transaktionen vermeiden, Isolation Level prüfen (RCSI?), Indizes für schnellere Suchen, ggf. Code-Optimierung um Sperrhaltedauer zu reduzieren.

(Andere)

30.000

9 %

Sonstige Wait-Typen (Summe vieler kleiner Anteile, z.B. ASYNC_NETWORK_IO, etc.)

Interpretation: Diese Wait-Statistik gibt einen klaren Hinweis auf die Hauptengpässe. CXPACKET dominiert mit 35 % – dies bestätigt, dass Parallelisierungs-Overhead eine große Rolle spielt. In SQL Server 2019 werden parallele Wartezeiten intern auf CXPACKET/CXCONSUMER aufgeteilt; hier zusammengefasst sieht man, dass viel Zeit darauf geht, dass parallel ausgeführte Threads wieder synchronisiert werden. Das ist typisch, wenn der Degree of Parallelism (DOP) hoch ist und/oder wenn viele kleine Aufgaben unnötig parallel laufen. Es untermauert die earlier CPU-Analyse: hohe CPU-Last gepaart mit CXPACKET-Waits weist oft darauf hin, dass man die Parallelität feiner einstellen sollte (z.B. MAXDOP limitieren, Cost Threshold erhöhen) und die betroffenen Abfragen auf Verbesserungsmöglichkeiten prüfen sollte (vielleicht verursachen sie Workload-Skew, bei dem ein Thread viel länger arbeitet als andere).

Der zweitgrößte Posten PAGEIOLATCH_SH (23 %) zeigt, dass ein erheblicher Teil der Wartezeit durch langsames Lesen von der Festplatte entsteht – das ist konsistent mit unseren Beobachtungen der I/O-Latenz und dem Wissen um große Scans ohne Index. Mit 23 % Anteil ist I/O-Wait signifikant, d.h. die Storage-Leistung bremst die Abfragezeiten ebenfalls. Die Maßnahme hier: Datentransfer verringern (durch Indizes, bessere Cache-Nutzung) oder schnellere Storage-Hardware.

WRITELOG mit 15 % legt nahe, dass auch das Transaktionslog ein gewisses Bottleneck darstellt. Viele parallele Commits warten auf Bestätigung des Log-Schreibens. Das ist typisch in Szenarien mit hoher Transaktionsrate; hier war es nicht der Top-Wait, aber doch relevant. Mögliche Ansätze: Log-Disk beschleunigen (SSD/NVMe), ausreichend große Log-Puffer und Dateigröße nutzen, oder die Transaktionsgestaltung prüfen (z.B. viele kleine Commits vs. Batch).

SOS_SCHEDULER_YIELD mit 7 % ist ein weiterer Indikator für CPU-Pressure: es bedeutet, Aufgaben hätten gerne weiter CPU genutzt, mussten aber abgeben und wieder anstellen – typisch wenn CPU ausgelastet ist. Zusammen mit dem hohen Signal-Wait-Anteil (> 20 %, aus Gesamtsicht) bestätigt das: CPU ist ein Engpass.

PAGELATCH_UP (9 %) zeigt moderate Latch-Waits, häufig in Verbindung mit TempDB oder bestimmten Schwerpunkten im Bufferpool. 9 % ist nicht trivial, aber auch nicht alarmierend. In unserem Fall kommt das wohl von TempDB-Allokationen, da 8 TempDB-Dateien im Einsatz sind und leichte PFS-Latch-Waits verzeichnet wurden. Es ist kein dominanter Engpass, aber sollte überwacht werden. Wäre dieser Wert deutlich höher, wäre Handlungsbedarf (mehr Dateien, Optimierung) gegeben.

LCK_M_X (Locks) mit 6 % deutet an, dass es einige Blockierungssituationen gab, aber vom Anteil her war das kein Hauptproblem. Gelegentliche Sperr-Wartezeiten kommen vor, z.B. wenn eine längere Schreib-Transaktion läuft und andere auf das Release warten. In unserem Beispiel könnten solche Locks z.B. von Wartungsjobs oder lange laufenden Updates kommen, die andere kurz blockieren.

In Summe erlauben die Wait-Statistiken den Blick auf das große Ganze: CPU/Parallelität und I/O sind klar als Hauptthemen erkennbar (CXPACKET, SOS_SCHEDULER vs. PAGEIOLATCH, WRITELOG). Kleinere Beiträge (Latch, Locks) sind da, aber sekundär. Wichtig ist, dass man Waits immer im Kontext sieht – hier passen sie sehr gut zu den vorher beobachteten Symptomen.

Abfrage-Ebene (Query-Analyse)

Beobachtungen: Auf der Ebene einzelner SQL-Abfragen wurden mittels Query Store und DMV (sys.dm_exec_query_stats und verwandte) die ressourcenintensivsten Queries ermittelt. Insbesondere wurden die Top-Abfragen nach CPU-Zeit und logischen Lesezugriffen identifiziert, da diese Metriken auf langfristige Belastung hindeuten. Die folgende Tabelle zeigt exemplarisch zwei auffällige Abfragen aus dem Zeitraum:

Query-Hash

Gesamt-CPU (ms)

Log. Reads

Dauer (ms)

Plan-Hinweis

Empfohlene Maßnahme

0xA1B2C3…

120.000

1.200.000

5.000

Index-Scan auf Tabelle Orders (kein Index auf Filter CustomerID) – führt zu Volltabellenscan.

Nonclustered Index auf Orders(CustomerID) anlegen.

0xDEADBE…

80.000

800.000

8.000

Suboptimaler Plan durch Parameter Sniffing bei Stored Proc usp_GetReport – für einige Parameter wird ein ineffizienter Plan (Nested Loops) genutzt.

Parameter Sniffing entschärfen (z.B. OPTION(RECOMPILE) oder Query Store Forced Plan).

(Beispieldaten: „Log. Reads“ = logische Lesevorgänge; Dauer = durchschnittliche Ausführungsdauer pro Ausführung bei den Top-Queries.)

Interpretation: Query 1 (Hash 0xA1B2C3…) stellt sich als Hauptverbraucher heraus. Es handelt sich um eine (fiktive) Abfrage auf der Orders-Tabelle mit Filter auf CustomerID, für die kein geeigneter Index vorhanden ist. Der Plan-Hinweis zeigt, dass der Optimizer einen Index-Scan (vermutlich des Clustered Index, also eigentlich ein Full Table Scan) durchführt. Das erklärt die enormen 1,2 Mio. logischen Lesezugriffe und die hohe CPU-Zeit – die gesamte Tabelle wird durchsucht. Diese eine Abfrage verschlingt 30 % der gesamten CPU-Zeit im Profil und treibt die I/O-Last. Maßnahme: Ein Nicht-Clustered Index auf CustomerID (ggf. inklusive relevanter Spalten als Included Columns) würde es ermöglichen, selektiv zu suchen, was CPU und IO dramatisch senken dürfte. Diese Abfrage korreliert direkt mit den zuvor gesehenen Indikatoren (hohe CPU, hohe PAGEIOLATCH).

Query 2 (Hash 0xDEADBE…) ist ebenfalls interessant: Hier scheint ein Parameter-Sniffing-Problem vorzuliegen. Der Query-Text (z.B. ein Stored Procedure usp_GetReport mit einem Parameter) läuft manchmal schnell, manchmal extrem langsam. Im Schnitt verbrauchte sie 80.000 ms CPU und 800k Reads – sehr hoch. Plan-Hinweis: Vermutlich wurde der Abfrageplan einmal für einen bestimmten Parameter kompiliert, der nicht repräsentativ für andere Fälle ist, wodurch für andere Parameter ein ineffizienter Plan (z.B. Nested Loops mit tausenden Iterationen oder ungeeigneter Indexnutzung) verwendet wird. Das führte zu deutlich mehr logischen Reads als nötig. Maßnahme: Parameter Sniffing entschärfen, etwa indem man OPTION RECOMPILE nutzt bei dem Problem-Statement oder per Query Store einen besseren Plan forciert, oder Code-Refaktorisierung (z.B. Aufteilen der Prozedur für verschiedene Fälle). Wichtig ist, dass der Plan konsistent wird.

Weitere Abfragen: Neben diesen Top-2 fanden sich noch andere Kandidaten – z.B. eine Masseneinfüge-Operation, die viel WRITELOG verursachte, und einige kleinere Abfragen mit vielen Key Lookups (aufgrund fehlender Covering Indizes). Diese tragen kumulativ weniger bei, sind aber ebenfalls Optimierungschancen. Allgemein zeigte die Query-Analyse, dass wenige „Hot Queries“ einen Großteil der Ressourcen verbrauchen (ein typisches 80/20-Phänomen). Das bietet die Möglichkeit, mit gezielten Optimierungen an diesen Abfragen einen großen Effekt zu erzielen.

Sperren, Blockierungen und Deadlocks

Beobachtungen: Während der Analyse wurden auch Sperrmetriken und Events betrachtet. PerfMon zeigte moderate Werte für Lock Waits/sec, und die durchschnittliche Wartezeit auf Locks blieb im zweistelligen Millisekundenbereich – kein Hinweis auf flächendeckende Sperrprobleme. Es traten vereinzelt Blockierungen (Blocking) auf: z.B. wurde morgens um 09:05 Uhr beobachtet, dass eine länger laufende Abfrage (der angesprochene Orders-Scan) eine exklusive Sperre auf einigen Pages hielt, wodurch parallel ein Update-Prozess kurz warten musste (ca. 5 Sekunden LCK_M_S-Wartezeit auf einen Shared-Lock). Diese Situationen waren jedoch selten und kurz. Deadlocks wurden im Zeitraum keine gravierenden aufgezeichnet (die System-Health-Extended Event Session zeigte 2 Deadlock-Grafen in einer Woche, beide mit geringem Schaden und gleichen beteiligten Abfragen – potenziell könnte man diese Abfragen noch prüfen, aber es war kein wiederkehrendes Muster ersichtlich).

Interpretation: Locking war in diesem Szenario kein dominanter Engpass. Die kurzen Blockierungen, die auftraten, hängen oft mit den schon identifizierten teuren Abfragen zusammen – eine Abfrage, die lange läuft, erhöht natürlich die Wahrscheinlichkeit, dass sie zwischenzeitlich jemand blockiert (oder selbst blockiert wird). In unserem Beispiel ließen sich die Blockierungen auf die Problemabfragen zurückführen: Wenn z.B. die Orders-Scan-Query läuft, könnte sie (je nach Isolation Level) Schreiber kurz blockieren. Da diese Situationen selten waren und schnell aufgelöst wurden, reicht es wahrscheinlich, durch die Optimierungen (Indizes etc.) die Laufzeit dieser großen Abfragen zu verkürzen – damit minimiert sich automatisch ihre Sperrhaltedauer.

Keine Hinweise fanden sich auf systematische Probleme wie eskalierende Lock-Eskalationen oder falsch gesetzte Isolation Levels. Die DB läuft im Standard Read Committed; RCSI (Read Committed Snapshot Isolation) war nicht aktiviert, was bedeutet, dass Leser Schreiber blockieren können. In OLTP-Umgebungen wird RCSI oft empfohlen, um Lesekonflikte zu vermeiden – das könnte man perspektivisch erwägen, allerdings erzeugt RCSI wiederum TempDB-Overhead. Da Blocking hier kein Kernproblem war, haben wir RCSI nur notiert, aber nicht als vordringliche Maßnahme eingeplant. Deadlocks waren nicht signifikant; die wenigen Einzelfälle könnte man durch kleinere Code-Anpassungen (z.B. konsistente Zugriffreihenfolge) beheben, falls überhaupt notwendig.

Indizes und Wartung

Beobachtungen: Die Datenbank-Wartung (Indexpflege, Statistiken) wurde überprüft. Einige große Indizes (z.B. der Clustered Index der Orders-Tabelle) wiesen hohe Fragmentierung auf – z.B. ~45 % avg_fragmentation_in_percent, bei einer Größe von mehreren GB. Laut Wartungsplan wird ein Reorganize/ Rebuild einmal wöchentlich durchgeführt, mit Schwellenwert 30 % Fragmentierung; offenbar war zum Analysezeitpunkt gerade einige Tage seit der letzten Wartung vergangen, sodass wieder Fragmentierung entstanden war. Weiterhin wurde festgestellt, dass die Statistiken auf einigen wichtigen Tabellen seit >6 Monaten nicht manuell aktualisiert wurden (nur automatische Hintergrund-Updates bei genügend Änderungen fanden statt). Die Missing Index DMVs lieferten diverse Vorschläge, von denen einer deckungsgleich mit unserer identifizierten Abfrage war: ein fehlender Index auf Orders(CustomerID) wurde vom System mit hohem Impact vorgeschlagen. Andere Vorschläge betrafen Kombinationen, die mit Vorsicht zu genießen sind (teils generieren DMVs auch redundant erscheinende Indizes).

Interpretation: Die Index- und Statistikpflege hat direkte Auswirkungen auf Performance. In unserem Fall ist ersichtlich, dass veraltete Statistiken möglicherweise suboptimale Abfragepläne begünstigt haben (z.B. könnte der suboptimale Plan bei usp_GetReport durch frische Statistiken vermieden werden). Daher ist eine Aktualisierung der Statistiken (FULLSCAN für große Tabellen) angeraten. Die Fragmentierung von ~45 % auf einer großen Tabelle kann die Scan-Performance etwas beeinträchtigen (mehr I/O durch ungeordnete Ausdehnung auf der Platte, potentiell mehr Page-Splits während Inserts). Da OLTP-Workloads aber meist zufällige Zugriffe machen, ist Fragmentierung nicht das dringendste Problem – dennoch sollte der Wartungsplan sicherstellen, dass diese großen Tabellen regelmäßig defragmentiert werden, bevorzugt in wartungsarmen Zeiten.

Wichtiger sind die fehlenden Indizes: Der identifizierte fehlende Index auf CustomerID ist ein eindeutiger Quick Win. Solche Indexempfehlungen sind mit Bedacht zu behandeln – man will nicht jede DMV-Empfehlung blind umsetzen, da Überschneidungen möglich sind – aber hier passt es exakt zur problematischen Query. Weitere Indizes sollte man nach Priorität setzen: z.B. wurde auch ein Index auf einer History-Tabelle empfohlen, die jedoch selten in kritischen Abfragen vorkommt – solche können warten.

Insgesamt war die Wartungsstrategie solide (es gab einen wöchentlichen Rebuild-Job, Auto-Stats war an). Wir haben aber erkannt, dass die Häufigkeit eventuell erhöht werden sollte (ggf. mittelfristig 2x pro Woche Reorganize für stark fragmentierte große Tables, sofern die Wartungsfenster es zulassen). Wichtig ist, Index-Änderungen und Statistik-Updates einzubetten, um die nun geplanten Optimierungen (neue Indizes, RCSI-Aktivierung etc.) mit passender Wartung zu begleiten.

Transaktionslog und VLFs

Beobachtungen: Das Transaktionslog der Hauptdatenbank (SalesDB) wurde analysiert auf mögliche Engpässe. Neben der schon erwähnten Wait-Statistik WRITELOG (15 % Wait-Anteil) wurden die Log-Eintragsraten und die physische Log-Dateistruktur betrachtet. Die SalesDB_Log.ldf hatte eine Größe von 10 GB, mit automatischem Wachstum um 100 MB. Im letzten Monat wuchs das Log etwa 20 Mal (Autogrowth-Ereignisse), da zu Spitzenzeiten viele kleine Commits anfielen. Dies resultierte in einer Fragmentierung des Logs in rund 500 VLFs (Virtual Log Files). Während der Spitzenlast wurden Log-Schreibraten um die 50 MB/s beobachtet. Die Recovery Model ist FULL mit Log-Backups alle 15 Minuten. Kein Datenverlust trat auf, aber in der Log-Performance waren kleinere Spitzen erkennbar (z.B. wenn ein Autogrow stattfand, war für einige Sekunden eine höhere Latenz im WRITELOG-Wait zu sehen).

Interpretation: Eine hohe Zahl an VLFs (hunderte bis tausende) kann die Log-Verarbeitung und Restore-Zeiten negativ beeinflussen. ~500 ist moderat, aber es wäre besser, die Anzahl zu reduzieren, indem man das Log proaktiv in größeren Schritten wachsen lässt oder es gleich größer fest einstellt. Die vielen Autogrowth-Ereignisse deuten darauf hin, dass das Log-Volume regelmäßig an die 10 GB-Grenze stieß und dann stückweise vergrößert wurde. Während eines Autogrowth muss SQL kurz warten, was man als Spike im WRITELOG-Wait sehen kann. Deshalb empfehlen sich größere Wachstums-Inkremente oder manuelle Vorab-Vergrößerung des Logs in einem Wartungszeitfenster, damit der tägliche Betrieb ohne ständige Autogrows auskommt.

Die Log-Schreibrate (50 MB/s) ist nicht an sich problematisch, solange das Storage das schafft (was bei einer modernen SSD der Fall sein sollte). Die ~8 ms Schreiblatenz, die wir gemessen haben, ist in Ordnung. Das heißt, das Log-Subsystem ist zwar gefordert, aber nicht völlig überfordert. Die Wartezeit WRITELOG (15 % der Gesamtwait) zeigt jedoch, dass das Commit-Waiting spürbar war – in einer synchronen Hochverfügbarkeitskonfiguration (siehe nächster Abschnitt) könnte das zusätzlich verzögert sein. Hier muss man also sowohl auf physische Log-Optimierung (wie erwähnt: große zusammenhängende Log-Datei, schnelle Platte) achten, als auch auf Transaktionsmuster: viele kleine Einzelschritte vs. batchweises Commit. In der Analyse zeigten sich keine extremen Ausreißer (z.B. kein langanhaltender Log-Stau), aber es ist ein Feld, wo durch einfache Konfigurationsänderungen (Größe, Growth) und gute Practices (häufige Log-Backups – was hier gegeben ist – und sinnvolle Transaktionsgrößen) stabile Verhältnisse geschaffen werden können.

Hochverfügbarkeit (HADR) und Replikation

Beobachtungen: Die Instanz ist Teil einer Always On Availability Group mit einem sekundären Replica. Der Betriebsmodus ist synchron (Synchronous Commit) für hohe Verfügbarkeit, beide Server stehen im gleichen Rechenzentrum (Latenz ~1 ms). In den Wait-Statistiken tauchte HADR_SYNC_COMMIT auf, allerdings mit geringem Anteil (~3 % der Wartezeit). Die Commit-Latenz zu der synchronen Replica lag im Schnitt bei ca. 2–3 ms laut sys.dm_hadr_database_replica_states (gemessen als Commit Time). Die Queues (Log Send Queue, Redo Queue) waren minimal – meist < 1 MB, was bedeutet, der sekundäre Server konnte Änderungen fast in Echtzeit mitschreiben und aufholen. Es wurden keine Warnungen für HADR-Verzögerungen registriert.

Interpretation: Die Verwendung von synchroner Replikation bedeutet, dass jede Transaktion erst als bestätigt gilt, wenn auch der sekundäre sie im Log geschrieben hat. Dies fügt der Latenz einer Transaktion immer den Weg zum zweiten Server hinzu. In unserem Fall ist diese zusätzliche Latenz sehr gering (1–3 ms), was kaum ins Gewicht fällt – daher war HADR hier kein signifikanter Bremsfaktor. Hätte der sekundäre Knoten eine höhere Latenz (z.B. in einem entfernten Standort) oder wäre er ausgelastet, könnte HADR_SYNC_COMMIT leicht zu einem Top-Wait werden. Bei uns jedoch ist das System gesund: die Synchronisation verläuft zügig und beeinträchtigt die Performance nicht stark.

Dennoch muss man HADR im Hinterkopf behalten: Sollte man Maßnahmen wie die Erhöhung des Workloads planen oder tritt doch mal Netzlatenz auf, kann sich dieser Aspekt bemerkbar machen. Falls Performance absolut kritisch und Latenz-sensibel wäre, könnte man erwägen, auf asynchrone Replikation umzuschalten – allerdings auf Kosten der garantierten Synchronität. Für unsere Analyse genügte die Feststellung, dass HADR stabil und mit minimalen Wartezeiten läuft. Die Indikatoren (Commit Time, Queue Längen) zeigen keinen Handlungsbedarf. Monitoring sollte aber bestehen bleiben, um etwaige Netzwerk- oder Replica-Performanceprobleme sofort zu sehen, da diese sich direkt auf den Primär beeinträchtigen würden.

Virtualisierung und Hostfaktoren

Beobachtungen: Der SQL Server läuft auf einer virtuellen Maschine (VM) mit 8 vCPUs und 64 GB zugewiesenem RAM (davon 32 GB an SQL, Rest für OS und anderen Anwendungen). Die VM hostet ausschließlich diese SQL-Instanz. Wir haben Host-Metriken wie CPU Ready Time geprüft: im VMware-Umfeld lag der durchschnittliche %Ready der VM bei ca. 5 % per vCPU während Spitzenzeiten (bei moderater Last ~1–2 %). Das deutet darauf hin, dass die VM bei hoher Auslastung gelegentlich auf die CPU-Zuteilung vom Host warten musste (Overcommitment auf dem Host). Die NUMA-Ausrichtung der VM entsprach einem Knoten (alle 8 vCPUs auf einer NUMA-Node des Hosts, was günstig ist, da Cross-NUMA-Access so vermieden wird). Die Stromsparmodi auf dem Host waren auf „Balanced“ eingestellt, nicht auf „Höchstleistung“.

Interpretation: Virtualisierung kann subtile Auswirkungen haben. Eine CPU Ready Time von ~5 % bedeutet, dass in Spitzen bis zu 5 % der Zeit die VM-CPUs bereit waren, aber der Hypervisor sie nicht sofort einplanen konnte – dies ist ein Anzeichen für Überbelegung oder zumindest Konkurrenz auf dem Host. 5 % ist moderat, aber spürbar; es entspricht z.B. einer möglichen zusätzlichen Latenz von einigen Dutzend Millisekunden hier und da. In einer kritischen Umgebung sollte man versuchen, diesen Wert unter ~2 % zu halten. Maßnahmen könnten sein: Den Host zu entlasten, der VM ggf. weniger aber dafür exklusivere vCPUs zu geben (manchmal verbessert Reduktion der vCPU-Zahl die Scheduling-Effizienz, je nach Host-Auslastung), oder auf einen Host mit mehr freien Ressourcen umzuziehen.

Positiv ist, dass keine NUMA-Misskonfiguration vorlag – alle vCPUs waren offenbar auf einem physischen NUMA-Knoten, was optimale lokale Speicheranbindung bedeutet. Wäre die VM größer als ein Sockel, müsste man auf vNUMA achten. Die Power-Policy „Balanced“ jedoch ist suboptimal für einen Datenbankserver: sie kann zu wechselnden CPU-Taktraten führen. Hier empfehlen wir klar, den Host (und VM, falls separat einstellbar) auf Höchstleistung zu stellen, um konsistente, maximale CPU-Frequenz sicherzustellen. Diese Änderung ist niedriger Aufwand und kann ein paar Prozent Performance bringen, vor allem bei schwankender Last.

Zusammengefasst sind die Virtualisierungsfaktoren in diesem Fall nicht hauptverantwortlich für das Problem, verstärken aber die Engpässe minimal (die CPU-Engpässe z.B. werden durch CPU-Ready etwas verstärkt). Durch Abstimmung mit dem Infrastruktur-Team (z.B. um sicherzustellen, dass die VM ausreichend Priorität/Reservierung hat, oder Host-Auslastung reduziert wird) kann man diese Nebenwirkungen reduzieren.

Ursachenanalyse (Warum traten die Engpässe auf?)

Aus den Detailbefunden lassen sich die Wurzeln des Performance-Problems klar herausarbeiten. Hauptursache war eine ineffiziente Nutzung der Ressourcen durch bestimmte Abfragen. Insbesondere wurde eine große Tabelle (Orders) immer wieder ohne passenden Index durchsucht, was zu exzessiver CPU- und I/O-Belastung führte. Diese einzelne Schwachstelle im Datenbankentwurf (fehlender Index) verursachte Vollscans und damit hohe Last, die den Server an seine Grenzen brachte.

Verstärkt wurde das Problem durch nicht optimierte Konfiguration im Bereich Parallelität: Der SQL Server führte viele Abfragen parallel aus (Standard-MaxDOP war z.B. 8, und der Cost Threshold mit 5 relativ niedrig), sodass auch vergleichsweise moderate Abfragen parallelisiert wurden. Dies führte zu hohem CXPACKET-Overhead und einem ineffizienten Einsatz der CPU-Kerne – Threads warteten aufeinander, was die ohnehin knappe CPU-Zeit weiter band. In anderen Worten: zu viel Parallelität bei suboptimaler Abfrage hat das CPU-Bottleneck verschärft.

Sekundäre Ursachen waren identifiziert, die jedoch im Vergleich eine geringere Rolle spielten: Die Speicherausstattung war grenzwertig – ausreichend, aber ohne große Reserve – wodurch die großen Abfragen temporär viele Seiten aus dem Cache drängten (sichtbar am PLE-Abfall). Dadurch mussten Folgebefehle wieder von Platte lesen, was die I/O-Belastung erhöhte. Ebenso war die Storage-Performance für sich genommen zwar nicht ungewöhnlich schlecht, aber im Zusammenspiel mit dem hohen Datenvolumen (ausgelöst durch die Vollscans) wurde die I/O-Latenz zum limitierenden Faktor für Abfragezeiten.

Weitere contributory factors: – Veraltete Statistiken auf der großen Tabelle könnten dazu geführt haben, dass der Query Optimizer das Ausmaß der Daten unterschätzte und einen schlechten Plan wählte. – Die Transaktionslog-Konfiguration (viele kleine Autogrowths) war suboptimal, was allerdings nur leichte Verzögerungen im Schreibdurchsatz verursachte. – Parallel ablaufende Wartungsjobs (z.B. ein Stats-Update, das noch in die frühen Geschäftszeiten hineinragte) führten kurzzeitig zu Blockierungen, was jedoch nur ein Nebenphänomen war. – Die synchronen HADR-Einstellungen fügten jeder Transaktion eine kleine Verzögerung hinzu. In Summe war dies nicht hauptursächlich, darf aber nicht ignoriert werden. – Virtualisierungs-Overhead (CPU-Ready) hat die Lage geringfügig verschlechtert, indem verfügbare CPU-Zeit nicht 100% effizient zum Gast durchkam.

Kurz gesagt: Ineffiziente Abfrage + fehlender Index -> extreme Ressourcennutzung (CPU + I/O) -> Engpässe wurden durch Parallelitäts-Defaults potenziert. Alles andere sind Faktoren, die man verbessern kann, um mehr Puffer zu haben, aber sie hätten allein das Problem nicht verursacht.

Der Großteil der identifizierten Engpässe lässt sich also darauf zurückführen, wie die Workload die Datenbank nutzt, und weniger auf generelle Unterdimensionierung. Mit fokussierten Optimierungen an Datenbankobjekten (Indizes, Statistiken) und SQL-Server-Einstellungen kann die vorhandene Hardware deutlich entlastet werden. Falls diese Maßnahmen nicht ausreichen, kämen immer noch Skalierungsüberlegungen (mehr CPU, schnellerer Storage, mehr RAM) zum Tragen – doch zuerst sollten die vermeidbaren Ineffizienzen beseitigt werden.

Maßnahmenplan (Empfehlungen und Prioritäten)

Auf Basis der Analyse wurde ein Maßnahmenplan erstellt, der konkrete Schritte zur Performance-Verbesserung vorschlägt. Die Maßnahmen sind nach Wirkung (erwarteter Performance-Gewinn), Aufwand (Implementierungsaufwand) und Risiko (potenzielle negative Auswirkungen) bewertet und entsprechend priorisiert. Zudem ist angegeben, wer typischerweise verantwortlich ist und in welchem Zeitrahmen die Umsetzung anzustreben ist:

Maßnahme

Wirkung

Aufwand

Risiko

Verantwortlich

Zeitrahmen

Index auf Orders(CustomerID) erstellen, inkl. nötiger Spalten

Hoch (eliminiert Vollscan, ~70 % weniger I/O)

Mittel (Analyse & Online-Indexbau)

Niedrig (etwas Zusatzspeicher; minimale Insert-Verlangsamung)

DBA-Team

Kurzfristig (sofort nächstmgl.)

Statistiken aktualisieren (vollständig für große Tabellen)

Mittel (bessere Pläne, verhindert falsche Schätzungen)

Niedrig (mit Wartungsskript)

Niedrig (keine Nebenw., etwas Wartungszeit)

DBA-Team

Kurzfristig (sofort / nächster Turnus)

Query-Optimierung für usp_GetReport (Param. Sniffing lösen)

Mittel (konstantere Laufzeiten, weniger Ausreißer)

Niedrig-Mittel (Code-Änderung, Tests)

Niedrig (kaum Risiko außer Code-Change)

Entwickler/DBA

Kurzfristig (2–4 Wochen)

Parallelität tunen: Erhöhe Cost Threshold for Parallelism (z.B. von 5 auf 50) und setze MAXDOP auf 4

Mittel (weniger CXPACKET Overhead, effizientere CPU-Nutzung)

Niedrig (Konfigurationsänderung)

Niedrig-Mittel (kleines Risiko: manche Abfragen laufen evtl. etwas langsamer single-threaded)

DBA (Absprache mit Architekten)

Kurzfristig (sofort, nach Business-Hours)

Transaktionslog vergrößern (z.B. feste Größe 20 GB, Growth 1 GB) und VLFs reduzieren

Mittel (vermeidet Stalls bei Autogrow, stabilere Log-Schreibzeiten)

Niedrig (im Wartungsfenster durchführen)

Niedrig (nur Speicherverbrauch auf Disk)

DBA-Team

Kurzfristig (geplant in nächster Wartung)

TempDB überprüfen: sicherstellen, dass 8 Dateien gleiche Größe behalten (notfalls angleichen)

Gering-Mittel (verhindert potenzielle PFS-Contention bei Spitzen)

Niedrig

Niedrig

DBA-Team

Kurzfristig (im laufenden Betrieb möglich)

Wartungsfenster optimieren: Intensive Jobs (Index Rebuild, Stats Update) außerhalb der Peak-Zeiten terminieren

Mittel (verhindert Kollison mit Nutzer-Workload, weniger Blockierungen)

Niedrig (Job-Schedule anpassen)

Niedrig

DBA/IT-Admin

Kurzfristig (Planung sofort, Umsetzung in 1 Woche)

Host-Power-Setting auf Höchstleistung stellen

Gering (gleichmäßigere CPU-Leistung, ~5 % Gewinn möglich)

Niedrig

Niedrig (höherer Stromverbrauch)

Infrastruktur-Team

Kurzfristig (Absprache & umsetzen)

CPU-Ressourcen erhöhen (VM auf 12 vCPUs oder schnellere Host-Hardware)

Hoch (linear mehr CPU-Power, falls Workload skaliert)

Mittel-Hoch (Kapazität, Lizenzkosten prüfen)

Niedrig (Performance-Verbesserung, aber Kosten)

IT Mgmt/Infrastruktur

Mittelfristig (Budgetierung nötig)

RAM-Ausbau (z.B. +32 GB der VM zuweisen)

Mittel (mehr Puffer -> weniger I/O, höhere PLE)

Mittel (Hardware/Budget)

Niedrig (mehr Cache i.d.R. positiv)

IT Mgmt/Infrastruktur

Mittelfristig (nächste Budgetrunde)

Schnelleres Storage für Daten (z.B. Umzug auf SSD/NVMe-Array)

Hoch (I/O-Latenzen deutlich reduziert)

Hoch (Investition, Migrationsaufwand)

Niedrig-Mittel (Planung und potenzielles Migrationsrisiko)

IT Mgmt/Infrastruktur

Mittelfristig (Projekt, 3–6 Monate)

CPU-Overcommit adressieren (VM-Placement anpassen, ggf. vCPU reduzieren)

Mittel (weniger CPU-Ready, bessere CPU-Effizienz)

Mittel (Analyse Host-Auslastung, Umtakten VMs)

Niedrig-Mittel (VM ggf. kurz neu starten)

Infrastruktur-Team

Mittelfristig (nach Analyse Host)

RCSI aktivieren (Read Committed Snapshot) für DB

Mittel (vermeidet Leser-Warten auf Schreiber, flüssigere OLTP)

Mittel (Testen Verträglichkeit, TempDB Impact)

Mittel (mehr TempDB Usage, anfänglich Beobachtung nötig)

DBA-Team/App-Team

Mittelfristig (nach Haupt-Ursachen fixen)

Async Commit erwägen falls Latenz <-> Verfügbarkeit neu bewertet wird

Mittel (eliminiert Commit-Waits, 1–3 ms schneller Commits)

Niedrig (Einstellung am AG)

Hoch (Risiko: Datenverlust bei Failover ohne Sync)

DBA/Architektur

Langfristig (nur falls Business es erlaubt)

(Maßnahmen priorisiert von schnell wirksamen „Quick Wins“ zu langfristigen Verbesserungen; kursiv = optional/zu prüfen)

Erläuterungen zur Prioritätensetzung: Die ersten Maßnahmen (Index, Statistiken, Query-Tuning, Parallelitätseinstellungen) adressieren direkt die Kernursachen und sind mit minimalem Aufwand umsetzbar – sie sollten sofort angegangen werden. Verbesserungen an Log und Wartungsplanung folgen, da sie leicht umzusetzen sind und die Stabilität erhöhen. Hardware-nahe Upgrades (CPU, RAM, Storage) wurden als mittelfristig eingestuft: Sie bringen erhebliche Vorteile, sind aber mit Planung und Kosten verbunden, daher als nächster Schritt falls nötig. Die letzten Punkte wie RCSI oder Async-Commit sind optionale Strategien, die man prüfen kann, falls nach den primären Maßnahmen noch Bedarf besteht – diese haben größere Implikationen und sollten nur erfolgen, wenn die vorherigen Schritte das gewünschte Ergebnis nicht vollständig erreichen.

Jede Änderung sollte mit den entsprechenden Teams abgestimmt werden. Wichtig ist auch, vor Umsetzung stets Backups/Wiederherstellungspläne bereit zu haben (z.B. bei Konfigurationsänderungen wie RCSI ein Backout-Plan) und nach Umsetzung eine gezielte Nachmessung durchzuführen, um die Wirksamkeit zu überprüfen.

Monitoring-Empfehlungen und Fazit

Monitoring: Um nachhaltig die Performance im Blick zu behalten, sollte ein kontinuierliches Monitoring etabliert werden. Folgende Aspekte und Metriken werden empfohlen:

  • Ressourcen-Counter (Überwachung in kurzen Intervallen, z.B. 1 Min): CPU (% Processor Time), Arbeitsspeicher (Target vs. Total Memory, PLE), I/O (Avg. Disk sec/Read, sec/Write, Disk Queue Length), Netzwerkauslastung (wenn relevant für verteilte DB).
  • SQL Server-spezifische Indikatoren: Batch Requests/sec, Page Life Expectancy (Trend über Tag/Woche), Buffer Cache Hit Ratio, Memory Grants Pending, Log Bytes Flushed/sec, u.a. Diese Werte können per PerfMon oder speziellen Tools (z.B. SQL Server Data Collector oder Drittanbieter-Monitoring) erfasst werden.
  • Wait-Statistiken: Regelmäßiges Auslesen von sys.dm_os_wait_stats (z.B. täglich resetten und protokollieren) um Veränderungen im Warteprofil festzustellen. Besonders die Top 5 Waits sollten beobachtet werden – ein Anstieg von z.B. WRITELOG oder PAGELATCH sollte Alarm geben.
  • Query Performance: Einsatz des Query Store (falls verfügbar, SQL 2016+) um langsamste oder häufigste Abfragen zu tracken. Alternativ ein regelmäßiger Snapshot von sys.dm_exec_query_stats und Tools wie sp_BlitzCache oder sp_WhoIsActive für Ad-hoc-Analysen. Hiermit erkennt man, ob neue „Problem-Queries“ auftauchen oder bekannte sich verschlechtern.
  • TempDB und Blocking: Überwachung der TempDB-Auslastung (Größe des Version Store, ggf. PerfMon: Temp Tables Creation Rate) und der Blockierungsereignisse. Alerts für längere Blockierungen (> X Sekunden) und Deadlocks (z.B. via Extended Events) sollten eingerichtet werden.
  • HADR: Falls relevant, Monitoring der Replication Health (Log Send Queue, HADR_SYNC_COMMIT wait) – ein sprunghafter Anstieg kann auf Netzwerkprobleme oder Secondaries hinweisen.
  • Host/VM: Überwachen der VM-Metriken (CPU Ready %, Memory Ballooning, etc.) insbesondere während Lastphasen, um frühzeitig Engpässe auf Hypervisor-Ebene zu sehen.

Alerts & Schwellenwerte: Es ist sinnvoll, Alarmierungen zu konfigurieren, z.B.: CPU > 90 % über 10 Minuten, PLE fällt unter 300 s, Memory Grants Pending > 0 für >1 Minute, Disk-Latenz > 20 ms über längere Zeit, oder Wait-Zeit bestimmter Typen über einem Schwellwert (z.B. Signal Wait > 20 %). Auch ein plötzlicher Anstieg von Deadlocks oder Blocked Processes sollte gemeldet werden.

Visualisierung & Baselines: Die erhobenen Daten sollte man in Dashboard-Form visualisieren (z.B. mit Grafana, Power BI oder SQL Sentry o.ä.), um Trends zu erkennen. Wichtig ist das Aufbauen von Baselines: Etablieren Sie z.B. monatlich eine Performance-Baseline (Durchschnitts- und Peak-Werte der zentralen Metriken). So kann man nachvollziehen, ob Änderungen (wie die oben vorgeschlagenen Maßnahmen) langfristig Wirkung zeigen und ob die Workload zunimmt. Trendanalysen ermöglichen eine proaktive Kapazitätsplanung – z.B. wenn man sieht, dass die CPU-Auslastung jeden Monat um 5 % steigt, kann man frühzeitig planen, wann ein Upgrade nötig wird.

Fazit

Die durchgeführte Performance-Analyse hat gezeigt, dass mit gezielten, datenbankseitigen Optimierungen erhebliche Engpässe behoben werden können. Insbesondere die Beseitigung ineffizienter Abfragepläne (durch Indexierung und Tuning) und das Tuning der SQL-Server-Konfiguration werden die CPU- und I/O-Last deutlich reduzieren. Hardwareseitige Maßnahmen können die Performance weiter verbessern, waren aber nicht die erste Stellschraube, da die vorhandenen Ressourcen vor allem durch vermeidbare Faktoren ausgelastet wurden.

Wichtig ist, alle Änderungen kontrolliert durchzuführen – idealerweise einzeln und mit Messung der Auswirkung nach der Umsetzung (um sicherzustellen, dass sie den erwarteten Effekt haben und keine negativen Nebenwirkungen). Dabei wurden keine größeren Risiken identifiziert: Die meisten vorgeschlagenen Schritte sind Best Practices, deren Nebenwirkungen (z.B. mehr Indexspeicher, potenziell leicht höherer Schreibaufwand) gut vertretbar sind. Natürlich sollte man vor tiefgreifenden Änderungen (wie Isolation Level oder HADR-Modus) die Implikationen mit den Stakeholdern abstimmen.

Mit dem vorgeschlagenen Maßnahmenpaket und einem aufmerksamen Monitoring ist die SQL-Server-Instanz gut gerüstet, um die aktuelle Last effizienter zu bewältigen. Gleichzeitig werden damit Reserven geschaffen, die künftiges Wachstum oder temporäre Spitzen abfedern. Durch das fortlaufende Monitoring und regelmäßige Performance-Reviews kann sichergestellt werden, dass auftretende Engpässe früh erkannt und adressiert werden – so bleibt die Datenbank performant und stabil, zum Nutzen aller abhängigen Anwendungen.

 

Weitere Beiträge zum Thema SQL Server

 

Azure SQL für IT-Entscheider

1. Management Summary Azure SQL bezeichnet eine Familie von Microsofts Cloud-Datenbankdiensten, die SQL Server-Technologie in Azure als Service bereitstellen. Dazu gehören Azure SQL Database (ein einzeldatenbankbasierter PaaS-Dienst für moderne Anwendungen), Azure SQL...

mehr lesen

Azure SQL für Entwickler

Management Summary Azure SQL (PaaS) bietet Softwareentwicklern eine fully-managed SQL-Plattform in der Cloud – mit integrierter Hochverfügbarkeit, automatischen Backups und einfacher Skalierbarkeit. Im Vergleich zu einer selbstverwalteten SQL Server-Instanz entfallen...

mehr lesen

NUMA – Grundlagen und Anwendung in SQL Server 2022

Grundlagen von NUMA (Non-Uniform Memory Access) Was ist NUMA?  NUMA (Nicht-uniformer Speicherzugriff) ist eine Architektur für Mehrprozessorsysteme, bei der jeder Prozessor über einen eigenen lokalen Arbeitsspeicher verfügt. Alle Prozessoren teilen sich zwar...

mehr lesen

Tutorial: SQL Server-Indizes für Entwickler

Einführung: Dieser Fachartikel richtet sich an Entwickler mit Grundkenntnissen in Microsoft SQL Server und bietet eine umfassende Einführung in das Thema Indizes. Wir beleuchten, was Indizes sind und warum sie für die Performance einer Datenbank entscheidend sind....

mehr lesen

Wartungspläne für Microsoft SQL Server

Management Summary Wartung sichert Verfügbarkeit und Datenintegrität: Geplante Wartungsarbeiten in SQL Server zielen darauf ab, die Verfügbarkeit von Datenbanken hoch zu halten und Datenintegrität zu gewährleisten. Sie minimieren Ausfallzeiten und Risiken und...

mehr lesen

Virtualisierung von SQL Server, Best Practices

Management Summary Virtualisierung von Microsoft SQL Server ermöglicht es Unternehmen, Datenbank-Workloads effizienter bereitzustellen und zu verwalten. Durch Konsolidierung mehrerer SQL-Server-Instanzen auf weniger Hardware steigern Organisationen die Auslastung und...

mehr lesen

Weitere Beiträge zum Thema

Azure SQL für IT-Entscheider

1. Management Summary Azure SQL bezeichnet eine Familie von Microsofts Cloud-Datenbankdiensten, die SQL Server-Technologie in Azure als Service bereitstellen. Dazu gehören Azure SQL Database (ein einzeldatenbankbasierter PaaS-Dienst für moderne Anwendungen), Azure SQL...

mehr lesen

Azure SQL für Entwickler

Management Summary Azure SQL (PaaS) bietet Softwareentwicklern eine fully-managed SQL-Plattform in der Cloud – mit integrierter Hochverfügbarkeit, automatischen Backups und einfacher Skalierbarkeit. Im Vergleich zu einer selbstverwalteten SQL Server-Instanz entfallen...

mehr lesen

Wartungspläne für Microsoft SQL Server

Management Summary Wartung sichert Verfügbarkeit und Datenintegrität: Geplante Wartungsarbeiten in SQL Server zielen darauf ab, die Verfügbarkeit von Datenbanken hoch zu halten und Datenintegrität zu gewährleisten. Sie minimieren Ausfallzeiten und Risiken und...

mehr lesen

SQL Server Monitoring / Überwachung

Das Monitoring von SQL Server ist entscheidend, um die Leistung und Stabilität Ihrer Datenbank zu gewährleisten. Durch kontinuierliche Überwachung können potenzielle Probleme frühzeitig erkannt und behoben werden, bevor sie sich negativ auf Ihre Anwendungen auswirken....

mehr lesen

Microsoft SQL Server Lizenzierung

Die Lizenzierung von SQL Server kann komplex und kostspielig sein. Eine optimierte Lizenzierungsstrategie kann jedoch erhebliche Kosteneinsparungen und eine effizientere Nutzung der Ressourcen ermöglichen. Als erfahrener Berater werde ich die wichtigsten Schritte und...

mehr lesen

Microsoft SQL Server Kostenoptimierung

Die Verwaltung und Optimierung der Kosten einer SQL Server-Umgebung ist entscheidend für die Effizienz und Rentabilität eines Unternehmens. Als erfahrener Berater weiß ich, dass eine gut durchdachte Strategie zur Kostenoptimierung verschiedene Aspekte der SQL...

mehr lesen

SQL Server Analysis Services (SSAS)

SQL Server Analysis Services (SSAS) ist ein leistungsstarkes analytisches Datenbankmodul, das für Entscheidungsunterstützung und Geschäftsanalysen verwendet wird. Als erfahrener Berater weiß ich, dass SSAS eine Schlüsselrolle bei der Bereitstellung von...

mehr lesen

Microsoft SQL Server Sicherheit

Die Sicherheit von Microsoft SQL Server ist von entscheidender Bedeutung, um die Integrität, Vertraulichkeit und Verfügbarkeit Ihrer Daten zu gewährleisten. Als erfahrener Berater weiß ich, dass eine umfassende Sicherheitsstrategie verschiedene Ebenen und Methoden...

mehr lesen

Hochverfügbarkeit für Microsoft SQL Server

Die Gewährleistung der Hochverfügbarkeit (HA) von Microsoft SQL Server ist entscheidend für die Geschäftskontinuität und die Minimierung von Ausfallzeiten. Als erfahrener Berater weiß ich, dass eine gut durchdachte HA-Strategie verschiedene Technologien und Best...

mehr lesen

Indexoptimierung Microsoft SQL Server

Die Indexoptimierung ist ein zentraler Bestandteil der Leistungssteigerung eines SQL Servers. Als erfahrener Berater weiß ich, dass eine gut durchdachte Indexstrategie entscheidend für die Effizienz und Geschwindigkeit von Datenbankabfragen ist. In diesem Beitrag...

mehr lesen

Optimierung Microsoft SQL Server

Die Optimierung eines SQL Servers ist ein komplexer Prozess, der verschiedene Aspekte der Datenbankleistung berücksichtigt. Ich beschreibe hier die wichtigsten Schritte und Methoden zur Optimierung eines SQL Servers.: Die Optimierung eines SQL Servers ist...

mehr lesen