SQL Server Performance Tuning – Das DBA-Handbuch

🔧 PERFORMANCE TUNING FÜR DBAS

SQL Server Performance Tuning

Das vollständige Handbuch für DBAs: Tools, Methoden und Best Practices zur Identifikation und Behebung von Performance-Engpässen – von DMVs über Query Plans bis zu Blocking und I/O.

🎯 Zielgruppe: SQL-Administratoren, Datenbankentwickler 📅 SQL Server 2016 – 2025

🔩 Das Toolkit – Werkzeuge für den DBA

Von eingebauten DMVs bis zu spezialisierten Community-Tools

📊 DMVs (Dynamic Management Views)

Das Herzstück der Performance-Analyse. DMVs liefern Echtzeit-Statistiken über laufende Abfragen, Indexnutzung, Waits, Locking und mehr. Sie sind die erste Anlaufstelle für jeden DBA.

sys.dm_exec_query_stats sys.dm_os_wait_stats sys.dm_db_index_usage_stats

🐚 Query Store

Der Query Store zeichnet historische Abfrageleistung auf und ermöglicht es, Planregressionen zu erkennen und Pläne zu erzwingen. Ab SQL Server 2025 auch auf lesbaren Secondaries verfügbar.

🔬 Extended Events

Moderne, leichtgewichtige Alternative zum Profiler. Erzeugt minimalen Overhead (ca. 80% weniger als Profiler) und ist hochgradig konfigurierbar – der empfohlene Standard für SQL Server 2016+.

📈 Performance Monitor (PerfMon)

Windows-Bordmittel zur Überwachung von CPU, Memory, Disk I/O und SQL Server-spezifischer Objekte wie Locks, Buffer Manager und Access Methods.

📋 Standard Reports

SSMS-Berichte zu Memory, Top Queries, Blocking – ohne Setup, direkt verfügbar.

📐 Query Plans

Grafische, XML- und Textpläne – die Visitenkarte jeder Abfrage.

⚙️ Database Tuning Advisor (DTA)

Analysiert Workloads und empfiehlt Indizes, Partitionierungen und materialisierte Sichten.

🛠️ msSQLTools (dtcSoftware)

Das PowerShell-Modul von dtcSoftware (Webseite: dtc-sql.de) bietet über 74 Funktionen für Performance-Analyse, Diagnose und Automatisierung – spezialisierte Cmdlets für AlwaysOn, Waiter-Analyse und Health-Checks.

# msSQLTools Beispiel: Health-Check und Waiter-Analyse 
Get-mssPerformanceWaiter -SqlInstance "PRODSQL01" -WaitMinutes 15 
Get-mssMissingIndexes -SqlInstance "PRODSQL01" -Database "AdventureWorks" 
Invoke-mssHealthCheck -SqlInstance "PRODSQL01" -OutputHtml

🧩 Weitere Community-Tools

dbatools: PowerShell-Modul für Migration, Index-Wartung, Backup-Restore.
sp_WhoIsActive: Adam Machanics berühmte Prozedur für aktuelle Session-Analyse.
Plan Explorer (SolarWinds): Kostenloses Tool zur detaillierten Analyse von Ausführungsplänen.

📊 DMVs – Die wichtigsten Queries für den DBA-Alltag

Praktische Abfragen zur sofortigen Performance-Analyse
-- 1. Aktuelle teure Abfragen (nach CPU) 
SELECT TOP 10 
t.text AS Query, 
qs.total_worker_time / qs.execution_count AS AvgCPU_ms, 
qs.total_logical_reads / qs.execution_count AS AvgReads, 
qs.execution_count 
FROM sys.dm_exec_query_stats qs 
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) t 
ORDER BY AvgCPU_ms DESC; 
 
-- 2. I/O-Waits (PAGEIOLATCH = I/O Engpass) 
SELECT wait_type, waiting_tasks_count, wait_time_ms 
FROM sys.dm_os_wait_stats 
WHERE wait_type LIKE 'PAGEIOLATCH%' OR wait_type LIKE 'WRITELOG%' 
ORDER BY wait_time_ms DESC; 
 
-- 3. Unbenutzte Indizes (0 Reads, viele Updates) 
SELECT OBJECT_NAME(i.object_id) AS TableName, i.name AS IndexName, 
s.user_seeks, s.user_scans, s.user_lookups, s.user_updates 
FROM sys.indexes i 
JOIN sys.dm_db_index_usage_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id 
WHERE s.user_seeks = 0 AND s.user_scans = 0 AND s.user_lookups = 0 
AND s.user_updates > 0 
ORDER BY s.user_updates DESC; 
 
-- 4. Fehlende Indizes (Missing index requests) 
SELECT 
mid.statement AS TableName, 
migs.avg_total_user_cost * migs.avg_user_impact * (migs.user_seeks + migs.user_scans) AS Impact, 
'CREATE INDEX idx_missing ON ' + mid.statement + ' (' + ISNULL(mid.equality_columns,'') + 
ISNULL(mid.inequality_columns,'') + ') INCLUDE (' + ISNULL(mid.included_columns,'') + ')' 
FROM sys.dm_db_missing_index_groups mig 
JOIN sys.dm_db_missing_index_group_stats migs ON migs.group_handle = mig.index_group_handle 
JOIN sys.dm_db_missing_index_details mid ON mig.index_handle = mid.index_handle 
WHERE mid.database_id = DB_ID() 
ORDER BY Impact DESC;
⚠️ Wichtige Einschränkung: DMVs setzen sich nach jedem SQL Server-Neustart zurück. Für aussagekräftige Statistiken sollten Sie mindestens 14 Tage Betriebszeit haben – sonst sind Index-Nutzungsstatistiken verfälscht.

🎣 SQL Server Profiler vs. Extended Events

Warum Extended Events heute die bessere Wahl sind

📼 SQL Server Profiler (deprecated)

  • Grafische Benutzeroberfläche, einfach zu starten
  • Nachteil: Hoher Performance-Overhead (5-15% CPU auf produktiven Systemen)
  • Wurde mit SQL Server 2012 als veraltet markiert
  • Nur für einmalige Diagnose geeignet, nicht für Dauerbetrieb

🚀 Extended Events (empfohlen)

  • Asynchrone, nicht-blockierende Architektur
  • Ca. 80% weniger Performance-Overhead als Profiler
  • Hochgradig konfigurierbar (Filter, Aktionen, Ziele)
  • Kann im Produktivbetrieb ohne Auswirkungen laufen
  • Ersetzt vollständig SQL Trace und Profiler
-- Extended Events Session für Deadlock-Aufzeichnung 
CREATE EVENT SESSION [TrackDeadlocks] ON SERVER  
ADD EVENT sqlserver.xml_deadlock_report 
ADD TARGET package0.event_file(SET filename = N'C:\XEvents\Deadlocks.xel') 
WITH (MAX_MEMORY = 4096 KB, EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS, 
MAX_DISPATCH_LATENCY = 30 SECONDS, STARTUP_STATE = ON); 
GO 
ALTER EVENT SESSION [TrackDeadlocks] ON SERVER STATE = START;

📈 Performance Monitor (PerfMon) – Die Systemperspektive

Wichtige Counters für SQL Server
ObjektCounterAussage
Processor% Processor TimeCPU-Auslastung (Gesamt) – Werte über 80% über längere Zeit kritisch
MemoryPage Life ExpectancySeitenlebensdauer im Buffer Pool – unter 300 Sekunden = RAM-Knappheit
PhysicalDiskAvg. Disk sec/Read, Avg. Disk sec/WriteLatenz – über 10-15 ms = I/O-Engpass[reference:0]
SQL Server:Buffer ManagerBuffer cache hit ratioDatenbankseiten im Cache – über 90% ist gut
SQL Server:LocksNumber of Deadlocks/secDeadlocks pro Sekunde – konstant >0 = Problem

📋 Standard Reports & Query Plans

Die schnellsten Einstiege ins Performance Tuning

📑 Standard Reports (SSMS)

Rechtsklick auf Server/Datenbank → Reports → Standard Reports. Mehr als 20 Berichte für:

  • Memory Consumption
  • Top Transactions by Age
  • Performance – Batch Execution Statistics
  • Object Execution Statistics
  • Blocking Transactions

🔍 Query Plans – Der Schlüssel zur Optimierung

Ausführungspläne zeigen genau, wie SQL Server eine Abfrage verarbeitet:

  • Grafisch: In SSMS, zeigt Operatoren mit Kostenprozenten
  • XML: Für detaillierte Analyse mit Drittanbieter-Tools
  • Estimated vs. Actual: Estimated = Optimierer-Schätzung, Actual = echte Laufzeit

Rote Pfeile auf Operatoren (Seek vs. Scan) sind erste Warnsignale.

⚙️ Database Engine Tuning Advisor (DTA)

Automatisierte Index- und Partitionierungsempfehlungen

Der DTA analysiert eine Workload (z. B. eine Profiler-Trace-Datei, eine T-SQL-Skriptdatei oder den Query Store) und gibt Empfehlungen für Indizes, Indexed Views und Partitionierungen. Besonders wertvoll für:

  • Identifikation fehlender Indizes
  • Erkennung überflüssiger Indizes
  • Optimierung der Index-Partitionierung
-- Workload aus Query Store für DTA exportieren 
SELECT q.query_id, qt.query_sql_text 
FROM sys.query_store_query q 
JOIN sys.query_store_query_text qt ON q.query_text_id = qt.query_text_id 
WHERE q.last_execution_time > DATEADD(day, -7, GETUTCDATE());
⚠️ DTA-Empfehlungen immer mit Vorsicht prüfen: Das Tool kann aggressive Index-Vorschläge machen. Testen Sie Änderungen immer zuerst in einer Nicht-Produktionsumgebung.

🔒 Blocking & Deadlocks – Sperrprobleme verstehen und lösen

Die häufigste Ursache für schlechte Performance in OLTP-Systemen

🛑 Blocking – Der leise Performance-Killer

Blockierung entsteht, wenn eine Transaktion Sperren auf Ressourcen hält, während andere darauf warten. Blockings-Probleme erkennen Sie mit:

  • sys.dm_exec_requests (Spalte blocking_session_id)
  • Standard Report "Blocking Transactions"
  • Extended Events mit blocked_process_report

💀 Deadlocks – Der tödliche Zirkel

Zwei Transaktionen warten gegenseitig auf die Ressourcen der anderen. SQL Server wählt einen "Victim" und killed eine der Transaktionen. Behebung durch:

  • Indizes optimieren (weniger Scan-Operatoren)
  • Zugriffsreihenfolgen standardisieren
  • Transaktionen kürzer halten
  • READ COMMITTED SNAPSHOT aktivieren
-- Aktuelle Blockings-Situation analysieren 
SELECT  
blocked.session_id AS Blocked_Session, 
blocked.blocking_session_id AS Blocker_Session, 
blocked.wait_type, blocked.wait_time, 
t.text AS Blocked_Query 
FROM sys.dm_exec_requests blocked 
CROSS APPLY sys.dm_exec_sql_text(blocked.sql_handle) t 
WHERE blocked.blocking_session_id > 0;

🗂️ Index Scans, Lookups & Unused Indexes

Die drei großen Index-Fallstricke im Überblick

📊 Index Scan vs. Index Seek

Seek: optimal – direkter Zugriff auf die gesuchten Zeilen über B‑Tree.
Scan: suboptimal – der Index wird vollständig durchlaufen (wie eine Tabelle).

Ein Index Scan tritt typischerweise auf, wenn die WHERE-Klausel nicht mit der Index-Struktur kompatibel ist oder der Optimierer eine hohe Selektivität annimmt.

📎 Lookups – Die versteckte Kostenfalle

Ein RID oder Key Lookup entsteht, wenn ein Nonclustered Index verwendet wird, aber zusätzliche Spalten aus der Tabelle benötigt werden. Diese Lookups sind teure Einzelzugriffe. Abhilfe: Include-Spalten im Index oder den Index als covering Index definieren.

🗑️ Unused Indexes

Nicht genutzte Indizes schaden: Sie verbrauchen Speicher, verlangsamen INSERT/UPDATE/DELETE und verlängern Backup-Zeiten. Identifizieren Sie sie mit sys.dm_db_index_usage_stats – wenn user_seeks/user_scans/user_lookups = 0 und user_updates > 0, ist der Index ein Kandidat für die Löschung.

-- Kandidaten für ungenutzte Indizes (nach 14+ Tagen Uptime) 
SELECT OBJECT_NAME(i.object_id) AS TableName, i.name AS IndexName, 
s.user_seeks, s.user_scans, s.user_lookups, s.user_updates, 
'DROP INDEX ' + QUOTENAME(i.name) + ' ON ' + QUOTENAME(OBJECT_SCHEMA_NAME(i.object_id)) + '.' + QUOTENAME(OBJECT_NAME(i.object_id)) AS DropCommand 
FROM sys.indexes i 
LEFT JOIN sys.dm_db_index_usage_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id AND s.database_id = DB_ID() 
WHERE i.type_desc IN ('NONCLUSTERED', 'CLUSTERED') 
AND (s.user_seeks + s.user_scans + s.user_lookups = 0 OR s.user_seeks IS NULL) 
AND s.user_updates > 0 
ORDER BY s.user_updates DESC;

💾 I/O-Bottlenecks – Das unterschätzte Problem

Wie Sie I/O-Engpässe erkennen und beheben

I/O-Probleme sind für bis zu 50% aller Performance-Probleme verantwortlich. Erkennungskriterien:

  • Wait-Statistiken: PAGEIOLATCH_* über 200 ms durchschnittliche Wartezeit
  • PerfMon-Latenzen: Avg. Disk sec/Read > 10-15 ms konsistent[reference:1]
  • Page Life Expectancy (PLE) unter 300 Sekunden (RAM-Engpass führt zu vermehrten I/Os)
-- I/O-Wait-Typen analysieren 
SELECT wait_type, waiting_tasks_count, wait_time_ms, max_wait_time_ms 
FROM sys.dm_os_wait_stats 
WHERE wait_type IN ('PAGEIOLATCH_SH', 'PAGEIOLATCH_EX', 'WRITELOG', 'IO_COMPLETION') 
ORDER BY wait_time_ms DESC;
💡 Lösungsansätze: Fehlende Indizes ergänzen, TempDB Splitting, Datenbanken auf schnellere SSDs/NVMe verschieben, Buffer-Pool vergrößern, In-Memory-OLTP für I/O-intensive Workloads.

🚀 msSQLTools – Der All-in-One-Performance-Ansatz

Entwickelt von dtcSoftware (dtc-sql.de)

Das PowerShell-Modul msSQLTools bündelt viele der oben beschriebenen Techniken in einer einfach verwendbaren Befehlszeile. Es analysiert DMVs, erstellt Health-Reports und kann in CI/CD-Pipelines integriert werden.

# msSQLTools für Performance-Tuning (Download über dtc-sql.de) 
 
# Blockings-Analyse mit Ausgabe der führenden Blockierer 
Get-mssBlockingTree -SqlInstance "PRODSQL01" 
 
# Kompletter Health-Check in HTML (inkl. Waits, Index-Statistiken, Blockings) 
Invoke-mssHealthCheck -SqlInstance "PRODSQL01" -OutputHtml "C:\Reports\Health.html" 
 
# I/O-Latenz über mehrere Instanzen hinweg auswerten 
Get-mssPerformanceWaiter -SqlInstance "PRODSQL01", "PRODSQL02" -WaitType "PAGEIOLATCH*" -WaitMinutes 60 
 
# Query Store automatisch aktivieren und Baseline erfassen 
Enable-mssQueryStore -SqlInstance "PRODSQL01" -Database "AdventureWorks" -MaxSizeMB 2048
📌 Fazit: msSQLTools ist ein erprobtes Toolset, das die Microsoft-Tools (DMVs, Query Store, XEvents) effizient orchestriert. Speziell für AlwaysOn, Waiter-Analyse und automatisierten Health-Check bietet es sofort nutzbare Lösungen. Weitere Informationen und Download auf dtc-sql.de.

✅ Best Practices – Tägliche DBA-Routine

  • Query Store auf allen produktiven Datenbanken aktivieren (insb. OLTP) – erfasst historische Leistungsdaten erkennt Regressionen automatisch.
  • Wöchentliche DMV-Checks für fehlende Indizes, ungenutzte Indizes und teure Abfragen durchführen.
  • Extended Events statt Profiler – vermeidet unnötigen Overhead auf Produktionssystemen.
  • PerfMon-Datensätze für kritische Counter (CPU, Memory, Disk, Locks) kontinuierlich sammeln – idealerweise 24x7.
  • Indexpflege regelmäßig durchführen (Fragmentation minimal halten).
  • Statistiken täglich aktualisieren (besonders bei größeren Datenänderungen).
  • TempDB optimieren: Mehrere Dateien gleicher Größe (Anzahl ≈ logische CPU-Kerne/2).
  • MsSQLTools nutzen – automatisierte Health-Reports und Waiter-Analyse sparen Zeit.
Alle Angaben ohne Gewähr. Basierend auf SQL Server 2016–2025.
Empfohlene Tools: msSQLTools (dtc-sql.de), dbatools, sp_WhoIsActive, Plan Explorer.