Wissen

Praxis-Artikel und Buchkapitel zu SQL-Performance, Sicherheit und Hochverfügbarkeit – alle frei verfügbar.

Beratung

Festpreis-Analyse mit Bericht und Handlungsempfehlung – oder strategische Begleitung bei Architektur, Migration und Hochverfügbarkeit.

Fachbücher

Die fünfbändige Reihe „Ulis SQL-Bibliothek“ – Band 1 verfügbar. Leseprobe herunterladen!

Tools

UB.SimSQL: SQL-Server-Lastsimulator mit regelbasierten Konfigurationsempfehlungen. Lokal, ohne Cloud, ohne Abo.

Schulungen

Online-Workshops zu Performance, Sicherheit und Entwicklung – kompakt, hands-on, ohne MOC-Folienschlacht.

Analyse-Methodik: – SQL Server Performance

von

Dieser Artikel ist ein Kapitel aus:
SQL Server Performance & Troubleshooting
Praxisleitfaden, ca. 600 Seiten

[ Hier bei Amazon bestellen ]
[ Mehr zum Buch ]

Table of Contents
2
3

Analyse-Methodik:

Vom Bauchgefühl zur Diagnose — strukturiert, reproduzierbar, DSGVO-konform

Warum Methodik wichtig ist — oder: "Mein Server ist langsam" ist keine Diagnose

"Mein Server ist langsam." Das ist der Satz, mit dem die meisten Performance-Gespräche beginnen. Nicht "usp_GetKundenUmsatz braucht seit dem letzten Deployment dreimal so lang" oder "PAGEIOLATCH_SH ist um 14 Uhr auf 60% gestiegen." Einfach nur: langsam.

Das ist ein Symptom. Kein Problem. Keine Diagnose. Und wer ohne strukturierte Methodik direkt zu den DMVs springt, wird mit hoher Wahrscheinlichkeit das finden, was er schon immer verdächtigt hat — und nicht die eigentliche Ursache. Der DBA schaut auf Indizes. Der Entwickler schaut auf seinen Code. Der Admin schaut auf die CPU-Last. Jeder optimiert das, was er kennt. Das nennt sich Bestätigungsfehler und ist der häufigste Grund warum Performance-Probleme monatelang bestehen bleiben.

Dieses Kapitel bringt das Phasenmodell, das sich durch die gesamten Kapitel 9 bis 19 wie ein roter Faden zieht, in eine operative Struktur. Es zeigt, wie man von der Nutzerbeschwerde über die strukturierte Datenerhebung zur testbaren Hypothese kommt — und wie man anschließend validiert, ob die Maßnahme wirklich gewirkt hat oder nur die Beschwerde verstummt ist.

Dazu kommt Collect-SqlPerf.ps1 als praktisches Werkzeug für die ersten Phasen, ein Blick auf DSGVO-Aspekte bei der Performance-Analyse und eine Voranschau auf die drei Fallstudien in Kapitel 32, 33 und 34 — an denen das Phasenmodell in der Praxis erprobt wird.

 

Hintergrund: Der Klassiker aus hundert Kundenprojekten

Das Muster ist immer dasselbe: IT-Leiter ruft an, Server sei seit "zwei Wochen" langsam. Auf die Frage "Seit wann genau?" kommt: "Irgendwann nach dem letzten großen Update." Auf "Welche Queries?" kommt: "Alle. Na ja, vor allem das ERP." Auf "Haben Sie eine Baseline?" kommt Schweigen.

Zwei Stunden später hat man herausgefunden, dass ein Deployment eine skalare Funktion eingeführt hat, die 280.000 Mal pro Query aufgerufen wird. Das hat mit CPU-Last zu tun, mit IO zu tun, mit Statistiken zu tun — aber ohne die Frage "Was hat sich geändert?" hätte man ewig auf PAGEIOLATCH-Werte geschaut.

 

Das Phasenmodell: Sechs Schritte von der Beschwerde zur validierten Lösung

Performance-Analyse folgt in der Praxis einem iterativen Zyklus aus sechs Phasen. Iterativ deshalb, weil Phase 4 regelmäßig zurück zu Phase 3 führt — eine Hypothese stellt sich als falsch heraus, man muss neu ansetzen. Das ist keine Niederlage, das ist Wissenschaft. Die Alternative — eine Maßnahme ohne Validierung einzuführen — ist Blindflug mit Adminrechten.

 

Phase

Name

Kernfrage

Werkzeuge

1

Situationsaufnahme

Was genau ist das Problem?

Gespräch, Error Log, Monitoring

2

Baseline erheben

Was ist normal auf diesem System?

Collect-SqlPerf.ps1, DMV-Snapshots

3

Top-Down-Analyse

Wo ist der Engpass?

Wait Statistics, IO, Memory, CPU

4

Hypothese formulieren

Was ist die wahrscheinlichste Ursache?

DMVs, Extended Events, Query Store

5

Maßnahme und Test

Behebt diese Maßnahme das Problem?

Testumgebung, A/B-Messung

6

Validierung

Hat es wirklich geholfen?

Vorher/Nachher-Messung, Nutzer-Feedback

Tab. 31.1 – Das sechsphasige Performance-Analyse-Modell

 

 

Abb. 31.1: Das iterative Phasenmodell: Situationsaufnahme → Baseline → Analyse → Hypothese → Maßnahme → Validierung

Phase 1: Situationsaufnahme — zehn Fragen die man zuerst stellen muss

Der häufigste Fehler in Phase 1: direkt mit der Diagnose beginnen. Bevor eine einzige DMV aufgerufen wird, müssen zehn Fragen beantwortet sein. Diese Fragen kosten fünf Minuten — und sparen manchmal zwei Stunden Irrweganalyse.

  • Was genau ist langsam? Konkret: welche Aktion, welche Applikation, welche Seite? "Alles" ist keine Antwort.
  • Seit wann? Ein klarer Zeitstempel macht den Unterschied — gab es ein Deployment, ein Windows-Update, ein Datenbankwachstum?
  • Ist das Problem reproduzierbar? Immer, oder nur manchmal? Sporadische Probleme sind schwieriger zu fassen als dauerhafte.
  • Zu welchen Zeiten tritt es auf? Morgens, mittags, beim Monatsabschluss, bei parallelen Batch-Jobs?
  • Wer ist betroffen? Alle Nutzer, oder bestimmte Rollen, bestimmte Niederlassungen, bestimmte Instanzen?
  • Hat sich etwas geändert? Code-Deployment, neue Nutzer, Datenwachstum, neue Reports, Hardware-Änderung?
  • Gibt es Fehlermeldungen? Im Error Log, in der Applikation, beim Nutzer auf dem Bildschirm?
  • Wie messen die Nutzer "langsam"? Gefühlt, oder konkrete Zahlen? "Früher 2 Sekunden, jetzt 25" ist messbar.
  • Gibt es eine Baseline oder ein Monitoring? Wenn ja: was zeigt es für den Problemzeitraum?
  • Was ist der Druck? Produktionskrise mit aktivem Ausfall, oder "nervt schon seit zwei Wochen"? Das bestimmt das Tempo.
  • Frage 10 klingt banal, ist aber entscheidend: Bei einer aktiven Produktionskrise geht Stabilisierung vor Diagnose. Erst die Lage beruhigen — durch Neustart eines Prozesses, Abbruch einer blockierenden Session, temporäres Rollback — und dann die Ursache untersuchen. Kapitel 14 (Blocking und Deadlocks) beschreibt genau dieses Triage-Muster.

    -- Phase 1: Erste Orientierung — Was passiert gerade?
    -- Aktive Sessions, Wait Types, Blocking auf einen Blick
    SELECT
        r.session_id,
        r.status,                              -- RUNNING, RUNNABLE, SLEEPING, SUSPENDED
        r.wait_type,                           -- Das ist die erste Diagnose-Information
        r.wait_time / 1000.0          AS WarteSekunden,
        r.blocking_session_id,                 -- > 0 bedeutet: jemand blockiert diese Session
        r.cpu_time / 1000.0           AS CPUSekunden,
        r.logical_reads               AS LogischeLeseop,
        r.total_elapsed_time / 1000.0 AS GesamtdauerSek,
        SUBSTRING(t.text, 1, 300)     AS AktuelleAbfrage
    FROM sys.dm_exec_requests r
    CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
    WHERE r.session_id <> @@SPID      -- eigene Diagnose-Session nicht anzeigen
    ORDER BY r.wait_time DESC;         -- die am längsten wartenden Sessions oben

    Wenn du blocking_session_id > 0 siehst und sich das über mehrere Sessions aufbaut: Das ist ein Blocking-Baum. Sofortmaßnahme vor aller weiteren Diagnose: die blockierende Root-Session identifizieren und entscheiden ob sie abgebrochen werden muss. Alles andere wartet.

    Phase 2: Baseline erheben — ohne Referenzwert ist alles Spekulation

    Kapitel 9 hat Baseline-Erhebung als eigenständiges Konzept eingeführt. Hier die operative Seite: Was erhebst du, wann, und wie interpretierst du es?

    Für neue Server ohne Baseline: Collect-SqlPerf.ps1 mindestens zweimal ausführen — einmal in einer ruhigen Phase, einmal in der Lastspitze. Diese zwei Snapshots sind die Anfangs-Baseline. In der dritten Woche: Trendanalyse. Ohne diesen Referenzwert ist jede Aussage über "auffällig" eine Meinung, keine Diagnose.

    -- Baseline-Snapshot: Wait Statistics + IO + Memory in einem Durchlauf
    -- Diese Query in ruhiger Phase UND in der Lastspitze ausführen

     

    -- Schritt 1: Wait Statistics — Hauptindikator für Engpass-Typ
    SELECT TOP 15
        GETDATE()                          AS Zeitstempel,
        wait_type,
        wait_time_ms,
        waiting_tasks_count,
        -- Prozentualer Anteil: das Wichtigste in der Wait Statistics Analyse
        100.0 * wait_time_ms / NULLIF(SUM(wait_time_ms) OVER (), 0) AS AnteilProzent
    FROM sys.dm_os_wait_stats
    WHERE wait_type NOT IN (
        -- Idle-Waits herausfiltern — sie verzerrten das Bild massiv
        'SLEEP_TASK', 'WAITFOR', 'BROKER_TO_FLUSH', 'BROKER_TASK_STOP',
        'CLR_AUTO_EVENT', 'DISPATCHER_QUEUE_DEMAND', 'FT_IFTS_SCHEDULER_IDLE_WAIT',
        'HADR_WORK_QUEUE', 'SQLTRACE_BUFFER_FLUSH', 'LOGMGR_QUEUE',
        'CHECKPOINT_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_TIMER_EVENT',
        'BROKER_EVENTHANDLER', 'ONDEMAND_TASK_QUEUE', 'XE_DISPATCHER_WAIT',
        'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', 'SNI_HTTP_ACCEPT')
    ORDER BY wait_time_ms DESC;

     

    -- Schritt 2: IO-Latenzen pro Datenbankdatei
    SELECT
        DB_NAME(vfs.database_id)                              AS Datenbank,
        mf.physical_name                                      AS Dateiname,
        vfs.io_stall_read_ms  / NULLIF(vfs.num_of_reads, 0)  AS AvgReadLatenzMs,
        vfs.io_stall_write_ms / NULLIF(vfs.num_of_writes, 0) AS AvgWriteLatenzMs,
        vfs.num_of_reads,
        vfs.num_of_writes
    FROM sys.dm_io_virtual_file_stats(NULL, NULL) vfs
    JOIN sys.master_files mf
        ON vfs.database_id = mf.database_id
        AND vfs.file_id    = mf.file_id
    ORDER BY (vfs.io_stall_read_ms + vfs.io_stall_write_ms) DESC;

     

    -- Schritt 3: Memory — Page Life Expectancy und Buffer Cache Hit
    SELECT object_name, counter_name, cntr_value
    FROM sys.dm_os_performance_counters
    WHERE counter_name IN (
        'Page life expectancy',
        'Buffer cache hit ratio',
        'Memory Grants Pending',
        'Target Server Memory (KB)',
        'Total Server Memory (KB)')
    ORDER BY counter_name;

     

    Tipp: Baseline-Zeitpunkte: Wann ist der beste Zeitpunkt?

    Lastspitze (typisch morgens 9–11 Uhr oder nach einem Batch-Lauf): Das ist die "Worst-Case-Baseline". Alles was darüber liegt, ist auffällig.

    Ruhephase (nachts oder am Wochenende): Die "Minimum-Baseline". Zeigt den Grundverbrauch ohne Nutzer-Last.

    Nach Änderungen: Nach jedem Deployment oder jeder Konfigurationsänderung — damit du sofort siehst ob sich etwas verschlechtert hat.

    Wöchentlich zur selben Zeit: Trendanalyse. Langsame Verschlechterungen über Wochen erkennt man nur mit regelmäßigen Vergleichs-Snapshots.

     

    Was ist ein "normaler" Wert ohne eigene Baseline? Richtwerte aus Kapitel 9 zur Orientierung: PLE über 4.000 Sekunden für normale OLTP-Systeme. IO Read-Latenz unter 5 ms (SSD/NVMe), unter 20 ms (Spindel). CPU Signal Wait unter 25% der Gesamtwartezeit. Diese Werte sind Anhaltspunkte, keine Absoluta — manche Systeme laufen gesund mit PLE von 800, andere haben Performance-Probleme bei PLE von 10.000.

    Phase 3: Top-Down-Analyse — der Engpass bekommt einen Namen

    Top-Down-Analyse bedeutet: vom Allgemeinen zum Speziellen. Zuerst Wait Statistics — die sagen dir, welche Ressource fehlt. Dann die betroffene Ressource im Detail. Dann die konkreten Abfragen. Nicht umgekehrt. Wer mit der schlechtesten Query beginnt, ohne zu wissen, ob das überhaupt das Problem ist, optimiert womöglich etwas, das null Auswirkung auf die Nutzer hat.

    Der Top-Down-Ansatz in sechs Schritten:

  • Schritt 1: Wait Statistics — was wartet SQL Server auf? Die Antwort bestimmt alle weiteren Schritte. PAGEIOLATCH → IO. RESOURCE_SEMAPHORE → Memory Grants. LCK_M_* → Blocking. SOS_SCHEDULER_YIELD → CPU-Sättigung. Kap. 9 enthält die vollständige Wait-Type-Übersicht.
  • Schritt 2: IO analysieren — Latenzen pro Datenbankdatei, Autogrowth-Ereignisse, TempDB-Nutzung. Kap. 10 für Details. Richtwerte: unter 5 ms für SSD/NVMe, unter 20 ms für Spindel.
  • Schritt 3: Memory prüfen — PLE, Buffer Cache Hit Ratio, Max Server Memory vs. tatsächliche Nutzung, Memory Grants Pending. Kap. 11 für den vollen Kontext.
  • Schritt 4: CPU prüfen — Kompilierungsrate, Signal Waits, MAXDOP-Konfiguration, Plan-Cache-Nutzung. Hohe Kompilierungsrate heißt CPU-Last auch ohne teure Queries — Kap. 18 erklärt warum.
  • Schritt 5: Blocking und Deadlocks — LCK_M_*-Waits quantifizieren, Blocking-Ketten identifizieren, Deadlock-Graph aus System Health Session auslesen. Kap. 14 für den vollständigen Diagnose-Workflow.
  • Schritt 6: Top-Queries — CPU, IO, Duration, Execution Count. Erst jetzt auf konkrete Abfragen schauen — und nur, nachdem die vorherigen Schritte die Richtung vorgegeben haben.
  • -- Phase 3, Schritt 6: Top-Queries nach CPU-Verbrauch (kumulativ)
    -- Erst nach den Wait Statistics ausführen — damit klar ist, ob CPU überhaupt das Problem ist
    SELECT TOP 20
        qs.execution_count,
        -- CPU pro Ausführung in Millisekunden:
        qs.total_worker_time / 1000 / qs.execution_count     AS AvgCPUms,
        qs.total_elapsed_time / 1000 / qs.execution_count    AS AvgDauerms,
        qs.total_logical_reads / qs.execution_count          AS AvgLogReads,
        qs.total_physical_reads / qs.execution_count         AS AvgPhysReads,
        -- Gesamtverbrauch: Häufigkeit mal Kosten — das wahre Bild
        qs.total_worker_time / 1000                          AS GesamtCPUms,
        DB_NAME(st.dbid)                                     AS Datenbank,
        OBJECT_NAME(st.objectid, st.dbid)                    AS Prozedur,
        SUBSTRING(st.text, 1, 200)                           AS QueryText
    FROM sys.dm_exec_query_stats qs
    CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
    WHERE qs.execution_count > 5   -- selten laufende Queries ignorieren
    ORDER BY qs.total_worker_time DESC;

    Wichtig: GesamtCPUms ist oft aufschlussreicher als AvgCPUms. Eine Query, die 5 ms kostet, aber 500.000 Mal pro Stunde läuft, verursacht mehr CPU als eine, die 10 Sekunden dauert, aber nur zweimal täglich. Kapitel 15 behandelt die Analyse von Ausführungsplänen für genau diese Fälle — inklusive wie man den Unterschied zwischen einem einzelnen teuren Plan und einem häufig aufgerufenen günstigen Plan erkennt.

    Der Top-Down-Ansatz verhindert die "Überoptimierung an der falschen Stelle". Wenn der dominante Wait Type ASYNC_NETWORK_IO ist — die Applikation wartet auf das Netzwerk — hilft kein Index der Welt. Dann ist es ein Applikationsproblem (Kap. 25, Kap. 30). Wer zuerst Wait Statistics schaut, wählt seinen Diagnose-Pfad mit System.

    Phase 4: Hypothese formulieren — messbar und falsifizierbar

    Eine Hypothese ist keine Vermutung. Sie ist eine testbare Aussage: "Wenn ich Index X anlege, sinken die PAGEIOLATCH_SH-Waits um mindestens 40% und die Durchschnittslatenz von usp_GetBestellung sinkt von 850 ms auf unter 200 ms." Das ist messbar. Das ist falsifizierbar. Das ist eine Hypothese.

    "Der Server braucht mehr RAM" ist keine Hypothese. "Max Server Memory auf 56 GB anheben erhöht den PLE von 1.200 auf über 3.000 und senkt PAGEIOLATCH_SH von 35% auf unter 10%" — das ist eine Hypothese.

    Die Qualität einer Hypothese bestimmt die Qualität der Analyse. Eine vage Hypothese führt zu einer vagen Maßnahme die man nachher nicht validieren kann. Dann ist der Server "irgendwie besser" — oder auch nicht, aber die Beschwerden haben aufgehört. Beides ist keine Diagnose.

     

    Warnung: Niemals mehrere Maßnahmen gleichzeitig

    Die häufigste Validation-Falle: Man legt drei Indizes an, aktiviert RCSI, erhöht Max Server Memory, und startet den Server neu — alles in einer Nacht. Am nächsten Tag ist es besser. Was hat geholfen?

    Niemand weiß es. Das nächste Mal, wenn das Problem wiederkehrt, weiß man immer noch nicht warum es damals geholfen hat.

    Die Regel: Eine Maßnahme pro Validierungs-Zyklus. In einer Produktionskrise bricht man diese Regel — aber bewusst, und man dokumentiert es.

     

    Extended Events spielen in Phase 4 ihre stärkste Rolle: gezielt für die identifizierte Problemklasse. Nicht eine "alles erfassen"-Session, sondern ein präzises Event, das die Hypothese bestätigt oder widerlegt. Kapitel 8 zeigt, wie man XE-Sessions konfiguriert. Hier ein typisches Beispiel für die Phase-4-Diagnose bei Parameter Sniffing:

    -- Phase 4: Gezielte Diagnose mit Extended Events
    -- Nur für die Hypothesen-Überprüfung aktivieren — danach sofort stoppen

     

    -- Szenario: Hypothese 'usp_GetBestellung hat einen schlechten Plan durch Parameter Sniffing'
    CREATE EVENT SESSION [ParamSniffDiagnose] ON SERVER
    ADD EVENT sqlserver.sql_statement_completed(
        ACTION(
            sqlserver.sql_text,
            sqlserver.plan_handle,
            sqlserver.database_name
        )
        WHERE sqlserver.like_i_sql_unicode_string(N'%usp_GetBestellung%')
           AND duration > 500000    -- nur Ausführungen über 500 ms erfassen
    ),
    ADD TARGET package0.ring_buffer(SET max_memory = 20480)  -- 20 MB reichen
    WITH (
        MAX_DISPATCH_LATENCY = 10 SECONDS,
        TRACK_CAUSALITY = OFF     -- Overhead minimieren
    );

     

    -- Session starten:
    ALTER EVENT SESSION [ParamSniffDiagnose] ON SERVER STATE = START;

     

    -- Nach der Diagnose: SOFORT wieder aufräumen
    ALTER EVENT SESSION [ParamSniffDiagnose] ON SERVER STATE = STOP;
    DROP EVENT SESSION [ParamSniffDiagnose] ON SERVER;

    Phase 5: Maßnahme testen — erst in der Testumgebung, dann in Produktion

    Die goldene Regel: Keine Maßnahme geht ungetestet in Produktion. Das klingt selbstverständlich und wird erschreckend oft ignoriert — weil "wir haben keine Testumgebung" oder "es ist dringend". Beides sind schlechte Ausreden.

    Wenn es keine Testumgebung gibt: Die kleinste akzeptable Alternative ist ein Maintenance-Window mit Rollback-Plan. Nicht schön, aber ehrlich. Was man nie machen sollte: eine Konfigurationsänderung oder einen Index in der Stoßzeit ohne jede Vorbereitung einführen.

  • Vor der Maßnahme: DMV-Snapshot nehmen (Wait Statistics, Top Queries, IO-Latenzen). Dieser Snapshot ist der Vorher-Wert.
  • Maßnahme in Testumgebung unter realistischer Last testen. "Realistisch" heißt: mit denselben Datenmengen und Aufrufmustern wie in Produktion.
  • Rollback-Plan bereit haben: Wie macht man die Maßnahme rückgängig? Index löschen ist einfach. sp_configure-Änderung zurückrollen auch. Applikations-Deployment zurückrollen ist aufwendiger.
  • Produktionsfenster wählen: In der ruhigsten Phase, mit vollständiger DBA-Aufmerksamkeit. Nicht freitags um 17 Uhr.
  • Monitoring für mindestens 30 Minuten nach der Maßnahme aktiv lassen — erst dann ist klar ob der neue Zustand stabil ist.
  • Phase 6: Validierung — hat die Maßnahme wirklich gewirkt?

    Validierung ist nicht "der Nutzer hat aufgehört zu klagen." Nutzer gewöhnen sich an langsame Systeme und klagen irgendwann nicht mehr — das ist kein Erfolg, das ist Resignation. Echte Validierung ist quantitativ.

    -- Vorher/Nachher-Vergleich: Wait Statistics Delta
    -- Snapshot VOR der Maßnahme in Temp-Tabelle speichern
    SELECT wait_type, wait_time_ms, waiting_tasks_count
    INTO #WaitVorher
    FROM sys.dm_os_wait_stats
    WHERE wait_type NOT IN ('SLEEP_TASK', 'WAITFOR', 'LOGMGR_QUEUE', 'CHECKPOINT_QUEUE');

     

    -- Maßnahme durchführen...
    -- Warten bis ausreichend Last gelaufen ist (mindestens 30 Minuten)

     

    -- Nachher-Vergleich: Delta berechnen
    SELECT
        n.wait_type,
        n.wait_time_ms - v.wait_time_ms      AS DeltaWaiteMS,
        n.waiting_tasks_count - v.waiting_tasks_count AS DeltaTasks,
        -- Anteil des Deltas am Gesamt-Delta:
        100.0 * (n.wait_time_ms - v.wait_time_ms)
            / NULLIF(SUM(n.wait_time_ms - v.wait_time_ms) OVER (), 0) AS AnteilProzent
    FROM sys.dm_os_wait_stats n
    JOIN #WaitVorher v ON n.wait_type = v.wait_type
    WHERE n.wait_time_ms > v.wait_time_ms    -- nur Waits die zugenommen haben
    ORDER BY DeltaWaiteMS DESC;

     

    DROP TABLE #WaitVorher;

     

    -- Query Store: Automatischer Vorher/Nachher-Vergleich (SQL Server 2016+)
    -- Kap. 19 erklärt Query Store im Detail — hier der schnelle Vergleich:
    SELECT TOP 20
        q.query_id,
        OBJECT_NAME(q.object_id)               AS Prozedur,
        rs.avg_duration / 1000.0               AS AvgDauerMS,
        rs.avg_cpu_time / 1000.0               AS AvgCPUms,
        rs.avg_logical_io_reads                AS AvgLogReads,
        rs.count_executions                    AS Ausfuehrungen,
        rs.first_execution_time,
        rs.last_execution_time
    FROM sys.query_store_query q
    JOIN sys.query_store_plan p        ON q.query_id      = p.query_id
    JOIN sys.query_store_runtime_stats rs ON p.plan_id    = rs.plan_id
    ORDER BY rs.avg_duration DESC;

    Query Store (Kapitel 19) ist der natürliche Partner für Phase 6: Er speichert Ausführungsstatistiken mit Zeitstempel und ermöglicht einen direkten Vergleich von "vor" und "nach" für jede Query — ohne manuelles Snapshot-Verwalten. Wenn Query Store aktiviert ist, ist es das erste Werkzeug für die Validierung.

    Collect-SqlPerf.ps1 — strukturierte Datenerhebung für Phase 1 bis 3

    Collect-SqlPerf.ps1 ist ein PowerShell-Script, das die Datenerhebung für die ersten drei Phasen des Analyse-Modells in einem Durchlauf automatisiert. Es sammelt alle relevanten DMV-Daten, speichert sie reproduzierbar mit Zeitstempel und liefert strukturiert erfasste Rohdaten als Grundlage für die Analyse — die eigentliche Diagnose leistet das Handwerkszeug aus den Kapiteln 8 bis 19 dieses Buchs.

    Das Script ist unter boddenberg.de verfügbar und wird dort gepflegt. Anhang F enthält Bezugsquelle und Installationshinweise. Der typische Ablauf bei einem Kundeneinsatz: Als erstes das Script ausführen, die erhobenen Daten sichern, und dann mit den Methoden aus den Kapiteln 8 bis 19 in die Analyse einsteigen. Das ist effizienter als zwei Stunden DMV-Abklopfen ohne Plan.

    Was Collect-SqlPerf.ps1 erhebt

     

    Bereich

    DMV / Quelle

    Was du bekommst

    Wait Statistics

    sys.dm_os_wait_stats

    Top-15 Wait Types mit Prozentanteil

    Top Queries CPU

    sys.dm_exec_query_stats

    Top-20 CPU-intensive Abfragen

    Top Queries IO

    sys.dm_exec_query_stats

    Top-20 IO-intensive Abfragen

    IO-Latenzen

    sys.dm_io_virtual_file_stats

    Latenz pro Datenbankdatei (Read + Write)

    Memory / PLE

    sys.dm_os_performance_counters

    Page Life Expectancy, Buffer Cache Hit Ratio

    Memory Grants

    sys.dm_exec_query_memory_grants

    Ausstehende und gewährte Memory Grants

    TempDB

    sys.dm_db_file_space_usage

    Version Store, interne Objekte, Dateiaufteilung

    Blocking

    sys.dm_exec_requests

    Aktive Blocking-Ketten mit Session-Details

    Index-Nutzung

    sys.dm_db_index_usage_stats

    Seeks vs. Scans, unbenutzte Indizes

    Fehlende Indizes

    sys.dm_db_missing_index_details

    SQL Servers Empfehlungen mit Verbesserungspotenzial

    Autogrowth

    sys.fn_trace_gettable

    Wachstumsereignisse der letzten 24 Stunden

    Konfiguration

    sys.configurations

    Alle sp_configure-Werte im Vergleich zu Empfehlungen

    Tab. 31.2 – Was Collect-SqlPerf.ps1 erfasst

     

     

    Hinweis: Professionelle SQL Server Analyse als Dienstleistung

    Wer die Datenerhebung und Analyse lieber in erfahrene Hände geben möchte: Ulrich B. Boddenberg bietet strukturierte SQL Server Performance-Analysen zum Festpreis an — mit vollständigem Befundbericht und priorisierten Handlungsempfehlungen.

    Weitere Informationen: https://www.boddenberg.de/sql-server-performance-analyse/

     

    Typische Verwendung

    # Collect-SqlPerf.ps1 — Grundlegende Verwendung
    # Voraussetzung: PowerShell 5.1+, SqlServer-Modul, Lesezugriff auf alle DMVs

     

    # Standard-Durchlauf: alle Metriken, ohne Query-Texte (DSGVO-konform)
    .\Collect-SqlPerf.ps1 -ServerInstance "MWSQL01" -Database "master"

     

    # Mit Ausgabepfad und eigenem Prefix für die Dateibenennung:
    .\Collect-SqlPerf.ps1 `
        -ServerInstance "MWSQL01" `
        -OutputPath "C:\Analyse\2024-03-15" `
        -Prefix "Musterwerk_Lastspitze"

     

    # Mit Query-Text-Erfassung — nur nach explizitem DSGVO-Opt-in!
    .\Collect-SqlPerf.ps1 `
        -ServerInstance "MWSQL01" `
        -IncludeQueryText `         # Query-Texte erfassen
        -IncludeLoginNames          # Login-Namen erfassen

     

    # Zwei Snapshots vergleichen (Vorher/Nachher-Analyse):
    .\Compare-SqlPerf.ps1 `
        -Baseline "C:\Analyse\Vorher\*.json" `
        -Current  "C:\Analyse\Nachher\*.json" `
        -OutputPath "C:\Analyse\Vergleich"

     

    # Ergebnis: HTML-Report + CSV-Dateien für Trendanalyse in Excel

    Kernlogik: DMV-Erhebung in PowerShell

    # Ausschnitt aus Collect-SqlPerf.ps1 — Kern-Erhebungslogik
    # Zeigt das Prinzip: strukturiertes Abfragen und Speichern der DMVs

     

    param(
        [string]$ServerInstance = "localhost",
        [string]$Database       = "master",
        [string]$OutputPath     = ".\SqlPerfOutput",
        [string]$Prefix         = "SqlPerf",
        [switch]$IncludeQueryText,       # Opt-in: DSGVO-relevant!
        [switch]$IncludeLoginNames       # Opt-in: DSGVO-relevant!
    )

     

    # Ausgabeverzeichnis erstellen falls nicht vorhanden
    $timestamp  = Get-Date -Format "yyyyMMdd_HHmmss"
    $outDir     = Join-Path $OutputPath "${Prefix}_${timestamp}"
    New-Item -ItemType Directory -Path $outDir -Force | Out-Null

     

    # Hilfsfunktion: SQL-Abfrage ausführen und als JSON speichern
    function Invoke-SqlAndSave {
        param([string]$Query, [string]$FileName)
        $result = Invoke-Sqlcmd -ServerInstance $ServerInstance `
                                -Database $Database `
                                -Query $Query `
                                -TrustServerCertificate
        $result | ConvertTo-Json -Depth 3 |
            Out-File (Join-Path $outDir "$FileName.json") -Encoding UTF8
        Write-Host "  OK: $FileName ($($result.Count) Zeilen)"
    }

     

    # Wait Statistics erheben:
    Invoke-SqlAndSave -FileName "wait_stats" -Query @"
    SELECT TOP 20 wait_type, wait_time_ms, waiting_tasks_count,
        100.0 * wait_time_ms / NULLIF(SUM(wait_time_ms) OVER (), 0) AS AnteilProzent
    FROM sys.dm_os_wait_stats
    WHERE wait_type NOT IN ('SLEEP_TASK','WAITFOR','LOGMGR_QUEUE','CHECKPOINT_QUEUE')
    ORDER BY wait_time_ms DESC
    "@

     

    # IO-Latenzen erheben:
    Invoke-SqlAndSave -FileName "io_latency" -Query @"
    SELECT DB_NAME(database_id) AS DB,
        io_stall_read_ms  / NULLIF(num_of_reads, 0)  AS AvgReadMS,
        io_stall_write_ms / NULLIF(num_of_writes, 0) AS AvgWriteMS
    FROM sys.dm_io_virtual_file_stats(NULL, NULL)
    "@

     

    # Query-Texte NUR mit explizitem Opt-in erfassen
    if ($IncludeQueryText) {
        Write-Warning "Query-Texte werden erfasst — DSGVO-Opt-in erforderlich!"
        Invoke-SqlAndSave -FileName "top_queries_text" -Query @"
        SELECT TOP 20 total_worker_time / execution_count AS AvgCPU,
            SUBSTRING(st.text, 1, 500) AS QueryText
        FROM sys.dm_exec_query_stats qs
        CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
        ORDER BY total_worker_time DESC
        "@
    }

     

    Write-Host "Collect-SqlPerf.ps1 abgeschlossen: $outDir"

    Das Script erzeugt am Ende eine strukturierte Ausgabe: JSON-Dateien pro Bereich und eine HTML-Übersichtsseite mit den rohen Messwerten. Diese Rohdaten sind der Startpunkt für die Analyse — kein Selbstläufer, aber eine vollständige Datengrundlage. Schlägt die Wait Statistics hohe PAGEIOLATCH_SH aus? Dann geht es mit Kapitel 10 weiter. Zeigt die PLE einen Abfall unter 1.000 Sekunden? Dann ist Kapitel 11 der nächste Halt.

    Typische Script-Ausgabe und Interpretation

    # Beispielausgabe (vereinfacht):
    # === Collect-SqlPerf.ps1 === 2024-03-15 14:32:18
    # Server: MWSQL01  |  SQL Version: 2019 (15.0.4312)  |  Cores: 8  |  RAM: 64 GB

     

    # [WAIT STATISTICS] Top 5 Wait Types:
    # 1. PAGEIOLATCH_SH   41.2%  → IO-Engpass, Lesen aus Storage
    # 2. LCK_M_S          18.7%  → Blocking durch Shared Locks
    # 3. SOS_SCHEDULER_YIELD 9.1% → CPU-Sättigung / Scheduler-Druck
    # 4. CXPACKET          7.3%  → Parallelismus (prüfen ob Signal Wait hoch)
    # 5. RESOURCE_SEMAPHORE 5.8% → Memory Grant Wartezeiten

     

    # [IO LATENCY] Datenbankdateien:
    # MeineDB.mdf  Read: 4.2 ms avg  Write: 2.1 ms avg  → OK (SSD-typisch)
    # MeineDB.ldf  Read: 1.1 ms avg  Write: 28.7 ms avg → Log-Write-Latenz hoch!
    # tempdb.mdf   Read: 3.1 ms avg  Write: 3.4 ms avg  → OK

     

    # [MEMORY] PLE: 3.847 Sekunden  (Richtwert OLTP: > 4.000) → leicht unter Normal
    # Buffer Cache Hit Ratio: 99.2%  → gut

     

    # [TOP QUERIES CPU]:
    # 1. Proc: usp_GetKundenUmsatz  CPU avg: 847 ms  Executions: 4.200/h
    # 2. Proc: usp_MonatsReport     CPU avg: 12.400 ms  Executions: 2/h

     

    # [EMPFEHLUNG] Schwerpunkt: Log-Schreib-Latenz + PAGEIOLATCH_SH
    # → Kap. 10 (IO-Performance), Kap. 2 (Storage Deep Dive)

    Die erhobenen Daten sind kein Selbstläufer — sie zeigen, wo zu schauen ist, nicht was zu tun ist. Aber sie ersetzen das stundenlange manuelle DMV-Abklopfen durch einen strukturierten Startpunkt und schaffen eine gemeinsame Datenbasis für das Gespräch mit dem Kunden.

    DSGVO und Performance-Analyse: Was erlaubt ist, was nicht

    Performance-Analyse auf Produktionssystemen greift auf technische Metadaten zu. Das ist grundsätzlich unproblematisch. Kritisch wird es an genau einer Stelle: wenn personenbezogene Daten in den erfassten Daten enthalten sind.

    Was unkritisch ist

    Wait Types, IO-Latenzen, CPU-Auslastung, PLE, Execution Counts, Query-Hashes (ohne Text), Ausführungspläne ohne Parameterwerte — all das sind rein technische Metadaten. Kein Personenbezug, keine DSGVO-Relevanz. Collect-SqlPerf.ps1 erfasst in der Standardkonfiguration ausschließlich diese unkritischen Daten.

    Was DSGVO-relevant ist

  • Query-Texte: Sie können Kundennamen, E-Mail-Adressen, Kundennummern oder andere personenbezogene Daten als Parameterwerte enthalten — auch, wenn der Query-Text selbst technisch ist.
  • Login-Namen: sys.dm_exec_sessions.login_name ist personenbezogen, wenn dahinter reale Nutzer-Accounts stehen.
  • Ausführungspläne mit Parameterwerten: Actual Execution Plans können Parameterwerte enthalten die personenbezogen sind.
  • Extended Events mit sql_text oder username: Diese Actions erfassen Query-Texte und Login-Namen direkt.
  • Query Store — sys.query_store_query_text: Speichert Query-Texte persistent. Zugriff muss auf DBA-Rollen beschränkt sein.
  •  

    Hinweis: DSGVO-konforme Performance-Analyse: Checkliste

    1. Vor der Analyse: Mit IT-Sicherheit und ggf. Betriebsrat abstimmen, welche Daten erfasst werden dürfen.

    2. Collect-SqlPerf.ps1 ohne -IncludeQueryText und -IncludeLoginNames ausführen (Standard).

    3. Wenn Query-Texte nötig sind: schriftliche Genehmigung einholen, Analyse-Daten nach Abschluss löschen.

    4. Extended Events: sql_text-Action nur aktivieren, wenn unbedingt nötig, Session sofort nach Diagnose löschen.

    5. Analyse-Ergebnisse nicht dauerhaft auf Berater-Laptops speichern — oder mit definierter Löschfrist.

    6. Bei externen Consultants: Datenschutzkonzept und Auftragsverarbeitungsvertrag vorab klären.

     

     

    Warnung: Query Store und persistente Query-Texte

    Query Store (Kap. 19) speichert Query-Texte persistent in der Datenbank — das ist sein Kernfeature. Das bedeutet: Wer Zugriff auf sys.query_store_query_text hat, kann Query-Texte lesen.

    In hochsensiblen Umgebungen: Zugriff auf sys.query_store_query_text auf db_owner oder eine spezifische DBA-Rolle beschränken. Normale Entwickler sollten diesen View nicht ohne weiteres abfragen können.

    Query Store Retention prüfen: Wie lange werden Query-Texte gespeichert? Standard ist 30 Tage. In manchen Umgebungen muss das kürzer sein.

     

    Extended Events: Welche Session für welches Problem?

    Kapitel 8 behandelt Extended Events vollständig — Session-Design, Target-Typen, Ring Buffer vs. File Target, Overhead-Abschätzung. Hier der operative Überblick: Welche XE-Session für welches Problem in der Diagnose-Phase 4?

     

    Problem

    XE-Event

    Key Action

    Sinnvolle Schwelle

    Langsame Queries

    sql_statement_completed

    sql_text, plan_handle

    duration > 1.000.000 µs (= 1 s)

    Deadlocks

    xml_deadlock_report

    keine nötig

    alle Deadlocks — System Health hat sie bereits

    Blocking

    blocked_process_report

    sql_text

    blocked_process_threshold > 5 s konfigurieren

    Memory Pressure

    memory_broker_shrink_notification

    keine nötig

    alle Ereignisse

    Autogrowth

    database_file_size_change

    database_name

    Wachstum > 0

    Parameter Sniffing

    sql_statement_recompile

    sql_text, recompile_cause

    alle Recompilations

    Kompilierungsrate

    sql_statement_starting

    sql_text

    Frequenz > 100/s verdächtig

    Tab. 31.3 – XE-Sessions für häufige Performance-Probleme in Phase 4

     

    Die System Health Session läuft immer und enthält Deadlock-Graphen, schwere Fehler, Memory-Druckereignisse und langlaufende Queries — standardmäßig aktiv, minimaler Overhead, keine Konfiguration nötig. Immer dort nachschauen, bevor eine eigene Session gestartet wird.

    -- System Health Session: Deadlock-Graphen der letzten Stunden auslesen
    -- Kein Setup nötig — läuft auf jeder SQL Server Instanz (2008+)
    SELECT
        xdr.value('(event/@timestamp)[1]', 'datetime2') AS Zeitstempel,
        xdr.query('.')                                    AS DeadlockGraph
    FROM (
        SELECT CAST(target_data AS XML) AS TargetData
        FROM sys.dm_xe_session_targets t
        JOIN sys.dm_xe_sessions s ON t.event_session_address = s.address
        WHERE s.name = N'system_health'
          AND t.target_name = N'ring_buffer'
    ) AS Data
    CROSS APPLY TargetData.nodes('//RingBufferTarget/event[@name="xml_deadlock_report"]')
        AS XEventData(xdr)
    ORDER BY Zeitstempel DESC;
    -- Ergebnis: XML-Deadlock-Graph — in SSMS auf den Link klicken für grafische Ansicht

    Der Analyse-Bericht: Struktur und Inhalt

    Ein Performance-Analyse-Bericht ist kein Monitoring-Dashboard. Er ist ein strukturiertes Dokument, das Befunde erklärt, priorisiert und mit konkreten Maßnahmen verbindet. Der Unterschied zwischen einem Report, den der IT-Leiter liest, und einem, den er in der Schublade vergräbt: Priorisierung und klare Handlungsempfehlung.

     

    Abschnitt

    Inhalt

    Länge

    Zielgruppe

    Executive Summary

    Top-3 Probleme, Risikobewertung, Priorisierung

    1 Seite

    IT-Leitung, Management

    Systemübersicht

    SQL Server Version, Hardware, Konfiguration

    1–2 Seiten

    DBA-Team

    Befunde

    Jeder Befund einzeln: Messwert, Auswirkung, Ursache

    3–8 Seiten

    DBA-Team, Entwickler

    Maßnahmenplan

    Priorisierte Maßnahmen: wer, was, bis wann, wie validiert

    1–2 Seiten

    Alle

    Anhang

    Ausführungsplan-Screenshots, DMV-Rohdaten, XE-Ergebnisse

    variabel

    DBA-Team

    Tab. 31.4 – Struktur eines Performance-Analyse-Berichts

     

    Priorisierung: Impact mal Aufwand

    Nicht jede Maßnahme ist gleich wertvoll. Die Impact-Aufwand-Matrix ist das einfachste und effektivste Priorisierungswerkzeug — und sie spart Kundengespräche, weil sie Erwartungen sofort kalibriert.

     

    Kategorie

    Beispiele

    Typischer Aufwand

    Erwarteter Impact

    Quick Win

    Max Server Memory setzen, RCSI aktivieren, sp_configure-Fehler korrigieren

    < 1 Stunde

    Hoch, sofort messbar

    Kurzfristig

    Fehlende Indizes anlegen, Statistiken aktualisieren, TempDB konfigurieren

    1–4 Stunden

    Hoch bis mittel

    Mittelfristig

    Stored Procedures refactoren, Wartungs-Jobs einrichten, Query Store aktivieren

    1–3 Tage

    Mittel, nachhaltig

    Langfristig

    Applikations-Refactoring, ORM-Optimierung, Hardware-Upgrade

    Wochen bis Monate

    Hoch, aber mit Vorlaufzeit

    Tab. 31.5 – Impact-Aufwand-Matrix für Performance-Maßnahmen

     

    Quick Wins zuerst — nicht, weil sie die wichtigsten sind, sondern, weil sie Vertrauen schaffen. Wenn Max Server Memory nach zehn Minuten Arbeit den PLE verdoppelt, ist der Kunde bereit für das Gespräch über das dreiwöchige Applikations-Refactoring. Ohne diesen Quick Win ist das Gespräch schwieriger.

    Checkliste: Die ersten 30 Minuten bei einem unbekannten Server

    Du wirst zu einem Server gerufen, den du nie gesehen hast. "Es ist langsam." Was machst du? Die folgende Checkliste gibt dir in 30 Minuten einen vollständigen Überblick — ohne Vorwissen über das System.

     

    Minute

    Aktion

    Werkzeug

    Ziel

    0–2

    Versionsinformation, Hardware, Uptime

    SELECT @@VERSION, sys.dm_os_sys_info

    Kontext verstehen

    2–5

    Aktive Sessions und Blocking

    sys.dm_exec_requests

    Akutes Problem erkennen

    5–10

    Wait Statistics (letzte 5 Min)

    sys.dm_os_wait_stats

    Engpass-Typ identifizieren

    10–15

    IO-Latenzen Datenbankdateien

    sys.dm_io_virtual_file_stats

    IO-Problem lokalisieren

    15–20

    Memory: PLE, Buffer Cache Hit

    sys.dm_os_performance_counters

    Memory-Druck prüfen

    20–25

    Top Queries CPU + IO

    sys.dm_exec_query_stats

    Hauptverdächtigen benennen

    25–30

    Konfiguration: sp_configure

    sys.configurations

    Offensichtliche Fehlconfig?

    Tab. 31.6 – Erste-30-Minuten-Checkliste für den unbekannten Server

     

    Nach 30 Minuten solltest du eine Hypothese haben. Nicht zwingend die Antwort — aber eine Richtung. "Der dominante Wait Type ist PAGEIOLATCH_SH, die IO-Latenz auf der Log-Datei liegt bei 28 ms — das ist der erste Befund." Das ist eine Hypothese, die dich in Phase 4 führt.

    -- Die ersten 3 Minuten: Systemkontext herstellen
    SELECT @@VERSION;

     

    SELECT
        cpu_count                          AS LogischeCPUs,
        hyperthread_ratio                  AS HyperthreadRatio,
        physical_memory_kb / 1024         AS PhysRAM_MB,
        sqlserver_start_time               AS SQLStartzeit,
        -- Uptime in Stunden:
        DATEDIFF(HOUR, sqlserver_start_time, GETDATE()) AS UptimeStunden
    FROM sys.dm_os_sys_info;

     

    -- Wichtige Konfiguration: die häufigsten Fehlkonfigurationen prüfen
    SELECT name, value_in_use, description
    FROM sys.configurations
    WHERE name IN (
        'max server memory (MB)',
        'min server memory (MB)',
        'max degree of parallelism',
        'cost threshold for parallelism',
        'priority boost',           -- sollte 0 sein — Kap. 5 erklärt warum
        'lightweight pooling',      -- sollte 0 sein
        'optimize for ad hoc workloads'
    )
    ORDER BY name;

     

    -- priority boost = 1? Das ist der erste Quick-Win: auf 0 setzen
    -- max server memory unrealistisch? Das ist Quick-Win Nr. 2

     

    Praxisbeispiel: Musterwerk GmbH: Erste 30 Minuten auf MWSQL01

    Beim ersten Besuch: Collect-SqlPerf.ps1 läuft in 4 Minuten durch. Ergebnis: PAGEIOLATCH_SH 38%, LCK_M_S 22%, PLE 3.200 (unter der üblichen Instanz-Richtwert von 4.000). IO-Latenz auf der Datenbankdatei: 6 ms Read, 4 ms Write — vertretbar.

    Hypothese: Reader/Writer-Blocking als primäres Problem, Memory leicht unter Druck. Keine IO-Katastrophe. Phase 4: Blocking-Analyse — welche Queries erzeugen LCK_M_S? Kapitel 32 zeigt den vollständigen Analyse-Verlauf für Musterwerk — mit Befund und Maßnahmenplan.

     

    Typische Analyse-Fehler — und wie man sie vermeidet

    Die häufigsten Fehler in Performance-Analysen sind keine technischen Fehler. Es sind methodische Fehler. Die Technik stimmt, die Methodik nicht.

    Fehler 1: Die langsamste Query optimieren

    "Die langsamste Query ist unser Hauptproblem." Das ist der häufigste Reflex — und oft falsch. Eine Query, die 10 Sekunden dauert, aber zweimal pro Tag läuft, verursacht 20 Sekunden CPU-Last täglich. Eine Query, die 50 ms dauert und 100.000 Mal pro Stunde läuft, verursacht 5.000 Sekunden CPU-Last täglich — Faktor 250 mehr. Wait Statistics und Top-Queries-nach-Gesamtverbrauch zeigen das richtige Bild.

    Fehler 2: Reaktiv statt proaktiv

    "Wir warten bis es wieder brennt, dann schauen wir nach." Das Problem: wenn es brennt, hat man keine ruhige Minute für eine sorgfältige Analyse. Und ohne Baseline-Snapshot aus der ruhigen Phase weiß man nicht, was "normal" war. Proaktive Baseline-Erhebung und regelmäßiges Monitoring kosten wenig — aber im Krisenmoment sind sie unbezahlbar.

    Fehler 3: "Der Server ist schuld"

    In etwa 70% aller Performance-Probleme, die ich in Kundenprojekten gesehen habe, war der Ursprung im Code — nicht in der Hardware oder Konfiguration. Trendforge Digital (Kapitel 34) ist das extremste Beispiel: tadellose Hardware, perfekte Konfiguration, katastrophale Applikation. Mehr Hardware hätte das Problem nicht gelöst — es hätte es nur teurer gemacht.

    Fehler 4: Keine Testumgebung

    "Wir haben keine Testumgebung." Das ist kein Naturgesetz, das ist eine Entscheidung. Und die Konsequenz ist: Produktion wird zur Testumgebung — mit allen Risiken. Wer keine Testumgebung hat und trotzdem Indizes anlegt, sp_configure ändert oder Code deployed, testet in Produktion. Das sollte eine bewusste Entscheidung sein, keine Selbstverständlichkeit.

    Fehler 5: "Mehr RAM löst alles"

    Hardware-Upgrades sind die teuerste Art, ein Software-Problem zu kaschieren. Sie helfen kurzfristig — aber ohne die eigentliche Ursache zu beheben, wächst das Problem weiter. 64 GB RAM statt 32 GB kauft vielleicht drei Monate Zeit. Danach ist der Buffer Pool wieder voll, der PLE wieder niedrig, und das Hardware-Budget ist weg. Sparfuchs & Partner (Kapitel 33) ist ein gutes Gegenbeispiel: dort war mehr RAM tatsächlich nötig — aber nur als Teil einer umfassenderen Maßnahme, nicht als Allheilmittel.

    Vorschau: Das Phasenmodell in der Praxis

    Die drei Fallstudien in Kapitel 32, 33 und 34 sind das Phasenmodell in Aktion. Jede Fallstudie zeigt einen anderen Problemtyp — und wie die strukturierte Analyse einen anderen Lösungsweg ergibt.

    Kapitel 32: Musterwerk GmbH — der normale Fall

    MWSQL01 ist das, was die meisten Admins kennen: solide Hardware, vernünftige Grundkonfiguration, aber über die Jahre gewachsene Probleme. TempDB-Contention, ein Parameter-Sniffing-Problem in der Hauptabfrage, ein fehlender Index der täglich 200.000 Table Scans produziert.

    Die Analyse nach dem Phasenmodell führt in drei Stunden zur vollständigen Diagnose. Die Maßnahmen sind überwiegend Konfiguration und Query-Optimierung — kein Hardware-Upgrade nötig. Das ist der typische Verlauf: was wie ein Hardware-Problem aussieht, ist meist ein Konfigurations- oder Code-Problem.

    Kapitel 33: Sparfuchs & Partner — der Horror-Fall

    BUCHSQL01 ist das, was man sich nicht vorstellen mag aber regelmäßig antrifft: jeder erdenkliche Fehler auf einmal. 4 vCPUs, 8 GB RAM, eine einzige Spindel für OS, TempDB, Datendateien, Log-Dateien und Backup. Max Server Memory auf 8.192 MB bei 8 GB physischem RAM — die Instanz konkurriert mit dem OS um denselben Speicher. IO-Latenz Write p95: 312 ms. Auf NVMe wären das 0,03 ms.

    Das Phasenmodell zeigt hier ein komplett überlastetes System bei dem praktisch jede Ressource gleichzeitig gesättigt ist. Die Triage-Entscheidung — was zuerst — ist schwieriger als bei Musterwerk. Die Maßnahmen umfassen Hardware, Konfiguration und Code — ohne eine davon wäre der Erfolg kurzlebig.

    Kapitel 34: Trendforge Digital GmbH — der Entwickler-Albtraum

    TFSQL01 ist der Ferrari mit Anhängerkupplung: 32 Kerne, 256 GB RAM, NVMe-Storage, SQL Server korrekt konfiguriert. Und trotzdem: Performance-Probleme. Wait Statistics zeigen ASYNC_NETWORK_IO und SOS_SCHEDULER_YIELD — Hinweise auf zu viele kleine Requests und CPU-Last durch Kompilierungen.

    Die Phase-4-Analyse enthüllt: N+1-Problem im ORM (2.400 Einzelabfragen statt einem Join), skalare UDF die 180.000 Mal pro Abfrage aufgerufen wird, fehlende WHERE-Klauseln in mehreren aktiven Abfragen. Die Lösung liegt vollständig auf Applikationsebene — der DBA kann das Problem analysieren, aber ohne Entwickler-Kooperation nicht lösen.

    Diagnose-Übersicht: Analyse-Methodik

     

    Warnung: Symptome: Wann ist strukturierte Methodik dringend nötig?

    Performance-Probleme kehren regelmäßig zurück, obwohl bereits Maßnahmen ergriffen wurden — ein Zeichen, dass die Ursache nie wirklich identifiziert wurde.

    Mehrere Maßnahmen wurden gleichzeitig ergriffen und man weiß nicht welche geholfen hat — Validierung fehlt.

    Keine Baseline vorhanden: Jede Aussage über "auffällig" ist eine Meinung, keine Diagnose.

    Jede Abteilung nennt eine andere Ursache: DBA sagt Index, Entwickler sagt Code, Admin sagt Hardware — klassisches Zeichen für fehlende strukturierte Analyse.

    Performance-Analysen enden ohne klare Empfehlung — das ist ein Methodikproblem, kein Technikproblem.

     

     

    Tipp: So misst du das: Baseline in einem Script

    — Dieses Script liefert die wichtigsten Baseline-Werte in einem Durchlauf.

    — Ausführen in ruhiger Phase UND in Lastspitze, Ergebnisse vergleichen.

     

    PRINT '=== Wait Statistics (Top 10) ===';

    SELECT TOP 10 wait_type, wait_time_ms,

    100.0 * wait_time_ms / SUM(wait_time_ms) OVER () AS Anteil

    FROM sys.dm_os_wait_stats

    WHERE wait_type NOT IN ('SLEEP_TASK','WAITFOR','LOGMGR_QUEUE')

    ORDER BY wait_time_ms DESC;

     

    PRINT '=== IO-Latenzen ===';

    SELECT DB_NAME(database_id) AS DB,

    io_stall_read_ms / NULLIF(num_of_reads,0) AS ReadMS,

    io_stall_write_ms / NULLIF(num_of_writes,0) AS WriteMS

    FROM sys.dm_io_virtual_file_stats(NULL,NULL)

    ORDER BY (io_stall_read_ms + io_stall_write_ms) DESC;

     

    PRINT '=== Memory: PLE und Buffer Cache ===';

    SELECT counter_name, cntr_value

    FROM sys.dm_os_performance_counters

    WHERE counter_name IN (

    'Page life expectancy','Buffer cache hit ratio',

    'Memory Grants Pending');

     

     

    Hintergrund: Typische Fehlinterpretationen bei der Methodik

    "Wir wissen was das Problem ist" — Die erstgenannte Ursache ist häufig falsch oder nur eine Schicht des eigentlichen Problems. Strukturierte Analyse erst, dann Hypothese.

    "Performance-Analyse dauert Wochen" — Erste belastbare Erkenntnisse sind in 30 Minuten möglich. Die Tiefenanalyse dauert länger, aber der erste Befund kommt schnell.

    "DSGVO verhindert Performance-Analyse" — Nein. Technische Metadaten (Wait Types, Latenzen, CPU-Auslastung) sind nicht personenbezogen. Nur Query-Texte und Login-Namen erfordern Opt-in.

    "Der Wert ist hoch, also ist er schlecht" — Ohne Baseline kein Vergleich. 85% CPU kann normal sein oder kritisch, je nach System und Tageszeit.

    "Wir haben das schon mal gemacht" — Performance-Analyse ist kein einmaliges Projekt. Systeme ändern sich: Datenvolumen wächst, Code ändert sich, Nutzerzahlen steigen.

     

     

    Tipp: Erste Gegenmaßnahmen: Was sofort hilft

    1. Collect-SqlPerf.ps1 ausführen — strukturierter Überblick in 5 Minuten.

    2. Wait Statistics Snapshot nehmen — Was wartet SQL Server gerade auf?

    3. Blocking prüfen: sys.dm_exec_requests, blocking_session_id > 0 suchen.

    4. Top-10-CPU-Queries identifizieren — sys.dm_exec_query_stats nach total_worker_time sortiert.

    5. Error Log lesen — Checkpoint-Verzögerungen, Memory-Pressure-Meldungen, I/O-Fehler?

    6. Konfiguration prüfen — priority boost = 1? Max Server Memory zu hoch? Kap. 5 für die Verbotsliste.

    Diese sechs Schritte dauern 15 Minuten und liefern in 80% der Fälle eine klare Diagnose-Richtung.

     

    Zusammenfassung

    Performance-Analyse ist kein Bauchgefühl-Sport. Das sechsphasige Modell — Situationsaufnahme, Baseline erheben, Top-Down-Analyse, Hypothese formulieren, Maßnahme testen, Validierung — gibt jedem Analyse-Auftrag eine Struktur, die reproduzierbar, kommunizierbar und verteidigbar ist.

    Die zehn Fragen der Situationsaufnahme kosten fünf Minuten und sparen oft Stunden Irrweganalyse. Ohne Baseline ist jede Aussage über "auffällig" eine Meinung. Die Top-Down-Analyse — Wait Statistics zuerst, dann die betroffene Ressource, dann die konkrete Query — verhindert die Überoptimierung an der falschen Stelle.

    Collect-SqlPerf.ps1 automatisiert die Datenerhebung der ersten drei Phasen und liefert strukturiert erfasste Rohdaten als Grundlage für die eigentliche Analyse — die dann mit den Methoden aus den Kapiteln 8 bis 19 erfolgt. DSGVO ist kein Hindernis für Performance-Analyse — technische Metadaten sind nicht personenbezogen. Query-Texte und Login-Namen erfordern Opt-in, der Rest nicht.

    Die fünf typischen Analyse-Fehler — falsche Query optimieren, reaktiv statt proaktiv vorgehen, Hardware als Allheilmittel, keine Testumgebung, keine Validierung — sind alle methodischer Natur. Das Phasenmodell verhindert sie systematisch.

    Extended Events, Query Store und System Health Session sind die Werkzeuge für Phase 4, wenn DMVs nicht reichen. Die System Health Session läuft immer — sie ist der erste Anlaufpunkt für Deadlock-Diagnose, Memory-Ereignisse und langlaufende Queries.

    Der Analyse-Bericht endet immer mit einer priorisierten Maßnahmentabelle nach dem Impact-Aufwand-Prinzip: Quick Wins zuerst, damit das Vertrauen wächst und der Spielraum für die tiefgreifenderen Maßnahmen entsteht.

     

    Ausblick auf Kapitel 32: Das Phasenmodell trifft auf die erste Fallstudie — Musterwerk GmbH. Von der Nutzerbeschwerde über den Collect-SqlPerf.ps1-Befund bis zum vollständigen Maßnahmenplan. Der normale Fall — der zeigt, dass normale Probleme mit Bordmitteln lösbar sind, wenn man weiß wie man sucht.

     

    Abb. 2: Eskalationsstufen der Analyse-Methodik

     

    Kapitel 32