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.
🔩 Das Toolkit – Werkzeuge für den DBA
📊 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
-- 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;
🎣 SQL Server Profiler vs. Extended Events
📼 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
| Objekt | Counter | Aussage |
|---|---|---|
| Processor | % Processor Time | CPU-Auslastung (Gesamt) – Werte über 80% über längere Zeit kritisch |
| Memory | Page Life Expectancy | Seitenlebensdauer im Buffer Pool – unter 300 Sekunden = RAM-Knappheit |
| PhysicalDisk | Avg. Disk sec/Read, Avg. Disk sec/Write | Latenz – über 10-15 ms = I/O-Engpass[reference:0] |
| SQL Server:Buffer Manager | Buffer cache hit ratio | Datenbankseiten im Cache – über 90% ist gut |
| SQL Server:Locks | Number of Deadlocks/sec | Deadlocks pro Sekunde – konstant >0 = Problem |
📋 Standard Reports & Query Plans
📑 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)
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());
🔒 Blocking & Deadlocks – Sperrprobleme verstehen und lösen
🛑 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
📊 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
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;
🚀 msSQLTools – Der All-in-One-Performance-Ansatz
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
✅ 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.