SQL-Server-io-latenz
I/O-Latenz hoch — Storage als Bottleneck
Wenn Millisekunden zu Sekunden werden: Latenz aus SQL-Sicht messen, Schwellenwerte kennen — und mit Zahlen statt Gefühl verhandeln.
Es ist Mittwochnachmittag, 14:23 Uhr. Bei Sparfuchs & Partner ist seit der großen Storage-Migration am Wochenende — neues SAN, „dreimal so schnell“, sagt das Datenblatt — die Buchungserfassung spürbar zäher als vorher. Der DBA misst nach: Das Transaktionslog der Hauptdatenbank antwortet im Schnitt mit 38 Millisekunden pro Schreibvorgang. Achtunddreißig. Auf dem alten, angeblich lahmen Storage waren es zwei. Der Anruf beim Storage-Team bringt den Satz, den jeder DBA auswendig kennt: „Bei uns ist alles grün.“ Und das stimmt sogar — auf dem Dashboard des Teams, das über alle LUNs und fünf Minuten mittelt, sieht alles bestens aus. Nur der SQL Server erlebt etwas anderes.
Willkommen beim Klassiker der Zuständigkeits-Grauzone. I/O-Latenz ist das Symptom, bei dem zwei Teams auf zwei verschiedene Dashboards schauen und beide Recht haben — und genau deshalb gewinnt hier, wer aus SQL-Sicht misst und mit belastbaren Zahlen verhandelt statt mit Gefühl. Dieser Beitrag aus dem SQL-Server-Notarztwagen (die Triage-Übersicht liegt unter /sql-server-notarztwagen/) gibt dir die Messmethodik, die Schwellenwerte zur Einordnung — und die Verzweigung, die vor jedem Storage-Streit stehen muss: Fordert der SQL Server zu viel an, oder antwortet das Storage zu träge?
Was bedeutet I/O-Latenz technisch?
Latenz ist die Antwortzeit eines einzelnen I/O-Vorgangs — wie lange dauert es, bis eine angeforderte Seite gelesen oder ein Log-Block geschrieben ist? Sie ist nicht zu verwechseln mit dem Durchsatz (wie viele Megabyte pro Sekunde durch die Leitung passen): Ein Storage kann gigantischen Durchsatz liefern und trotzdem miserable Latenz haben — etwa wenn jede einzelne Anfrage erst durch eine Warteschlange, ein Tiering und zwei Virtualisierungs-Schichten muss. Für eine OLTP-Datenbank ist fast immer die Latenz die kritische Größe, denn dort warten tausende kleiner Vorgänge einzeln auf ihre Antwort.
Der SQL Server erlebt Latenz an zwei Fronten mit unterschiedlicher Härte. Front eins: Lesen von Datenseiten — sichtbar als PAGEIOLATCH_*-Wartetypen. Das tut weh, ist aber durch den Buffer Pool gedämpft: Was im Cache liegt, braucht die Platte nicht. Front zwei ist gnadenloser: das Transaktionslog. Jeder Commit wartet synchron darauf, dass seine Log-Blöcke physisch geschrieben sind — sichtbar als WRITELOG-Wartetyp. Eine Log-Latenz von 38 statt 2 Millisekunden heißt: Jede einzelne Buchung im Haus ist 36 Millisekunden langsamer, ohne dass irgendeine Abfrage schlechter geworden wäre. Deshalb gelten für das Log auch die strengeren Schwellenwerte.
Und das „alles grün“-Phänomen aus der Lead-Szene? Es ist kein böser Wille, sondern Mathematik: Storage-Dashboards mitteln über alle LUNs und über Minuten — die 38-ms-Spitzen einer einzelnen Log-LUN verschwinden im Durchschnitt von hundert unauffälligen Volumes. Der SQL Server dagegen misst exakt das, was seine Dateien erleben. Beide Messungen stimmen; sie beantworten nur verschiedene Fragen. Die Konsequenz für dich: Verhandle nie mit dem Bauchgefühl, sondern mit der Datei-genauen Messung aus sys.dm_io_virtual_file_stats — gegen die ist das Sammel-Dashboard argumentativ chancenlos.
|
Abgrenzung: Latenz vs. Durchsatz Latenz — Antwortzeit pro Vorgang, gemessen in Millisekunden. Die kritische Größe für OLTP: Jeder Commit, jeder Seitenzugriff wartet einzeln. |
|---|
Der Diagnose-Pfad: messen, einordnen, verzweigen
Der folgende Pfad führt vom Wait-Stats-Befund über die Datei-genaue Messung und die Schwellenwerte zur entscheidenden Verzweigung — und die Pointe steht ganz unten: Beide Verdächtigen können gleichzeitig schuldig sein.

Erste Diagnose: konkrete Skripte
Schritt 1 — Latenz pro Datei, als Delta gemessen
Die DMV zählt kumulativ seit dem Dienststart — auf einem Server mit Monaten Uptime versteckt sich das Problem von heute im Durchschnitt der Geschichte. Deshalb immer als Delta messen:
|
— Snapshot 1 wegschreiben SELECT database_id, file_id, num_of_reads, io_stall_read_ms, num_of_writes, io_stall_write_ms INTO #io1 FROM sys.dm_io_virtual_file_stats(NULL, NULL);
WAITFOR DELAY '00:05:00'; — 5 Minuten typische Last abwarten
— Snapshot 2 ziehen und Delta-Latenz berechnen SELECT DB_NAME(v.database_id) AS Datenbank, mf.type_desc AS Dateityp, mf.physical_name, v.num_of_reads – s.num_of_reads AS Reads, CASE WHEN v.num_of_reads – s.num_of_reads = 0 THEN 0 ELSE (v.io_stall_read_ms – s.io_stall_read_ms) / (v.num_of_reads – s.num_of_reads) END AS ReadLatenzMs, v.num_of_writes – s.num_of_writes AS Writes, CASE WHEN v.num_of_writes – s.num_of_writes = 0 THEN 0 ELSE (v.io_stall_write_ms – s.io_stall_write_ms) / (v.num_of_writes – s.num_of_writes) END AS WriteLatenzMs FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS v JOIN #io1 AS s ON s.database_id = v.database_id AND s.file_id = v.file_id JOIN sys.master_files AS mf ON mf.database_id = v.database_id AND mf.file_id = v.file_id ORDER BY WriteLatenzMs DESC; |
|---|
Lies das Ergebnis gegen die Richtwerte aus der Skizze: Datendateien unter 20 ms gesund, über 50 ms ein Problem; das Log unter 5 ms gesund, über 10 ms ein Problem — auf Flash-Storage entsprechend strenger. Und achte auf die Verteilung: Leidet genau eine Datei auf einem bestimmten Volume, ist das eine heiße Spur (falsche LUN, falsches Tier); leiden alle Dateien gleichmäßig, klemmt eher der gemeinsame Pfad.
Schritt 2 — Die Gegenprobe: Wer fordert das I/O an?
|
— Top-Queries nach physischen Reads (Verdacht A pruefen) SELECT TOP (10) qs.total_physical_reads AS PhysReads, qs.execution_count AS Ausfuehrungen, qs.total_physical_reads / qs.execution_count AS PhysReadsProLauf, SUBSTRING(st.text, (qs.statement_start_offset/2)+1, ((CASE qs.statement_end_offset WHEN -1 THEN DATALENGTH(st.text) ELSE qs.statement_end_offset END – qs.statement_start_offset)/2)+1) AS StatementText FROM sys.dm_exec_query_stats AS qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st ORDER BY qs.total_physical_reads DESC; |
|---|
Stehen hier zwei, drei Scan-Monster, die pro Lauf Millionen physischer Reads erzeugen, ist das Storage womöglich nur das Opfer — Verdacht A aus der Skizze. Die dritte Probe läuft auf dem Betriebssystem: Perfmon mit Avg. Disk sec/Read und Avg. Disk sec/Write je Volume. Decken sich die OS-Werte mit den SQL-Werten, liegt das Problem unterhalb des SQL Servers; sind sie deutlich besser, frisst eine Schicht dazwischen die Zeit (Filtertreiber, Virenscanner, Virtualisierung). Für den belastbaren Beweis gegenüber dem Storage-Team schließlich DiskSpd: ein definierter Lasttest auf einem Test-Volume derselben Storage-Klasse, mit dokumentierten Parametern — die gemeinsame Sprache, auf die sich beide Teams einigen können.
Die häufigsten Ursachen — und was du dagegen tust
1. Der Server liest zu viel — das Storage ist nur das Opfer
Bevor das Storage-Team auch nur angerufen wird, gehört Verdacht A abgearbeitet: Scans ohne passende Indizes pumpen ganze Tabellen durch den Buffer Pool, Memory Pressure sorgt dafür, dass Gelesenes sofort wieder verdrängt und neu gelesen wird, und Spill-Lawinen fluten tempdb mit Schreib-Lese-Zyklen, die nie geplant waren. In allen drei Fällen ist die hohe Latenz teilweise selbstgemacht: Ein überlastetes Storage antwortet träge, weil es mit unnötiger Arbeit geflutet wird. Die Querverweise: Top-Queries zähmen (Diagnose-Pyramide), Buffer Pool prüfen (Memory-Pressure-Beitrag), Spills beheben (tempdb-Beitrag). Erst danach ist der Anruf beim Storage-Team fair — und argumentativ wasserdicht.
2. Das Log-Laufwerk bremst jeden Commit
Die Lead-Szene: WRITELOG-Waits dominieren, die Log-Latenz liegt jenseits der 10 ms. Weil jeder Commit synchron auf das Log wartet, skaliert dieser Schmerz mit der Transaktionsrate — eine Buchungsanwendung mit tausend Commits pro Sekunde wird durch träges Log-Storage flächendeckend gelähmt, ganz ohne schlechte Abfragen. Therapie in Stufen: Das Log gehört auf die latenzärmste Storage-Klasse im Haus (Flash, eigenes Volume, kein Tiering — Logs schreiben sequenziell und sind für Auto-Tiering-Heuristiken ein Albtraum). Auf Anwendungsseite hilft, Commit-Gewitter zu vermeiden: Wer in einer Schleife zehntausendmal einzeln committet, bezahlt zehntausendmal die Log-Latenz — Batching senkt die Rechnung drastisch.
3. Geteiltes Storage — der laute Nachbar und das Tiering
Auf einem gemeinsam genutzten SAN konkurriert deine Datenbank mit allem, was sonst noch dort wohnt: dem Fileserver, der VDI-Farm, dem Backup, das nachts alles wegsaugt. Das Symptom sind Latenz-Wellen zu wiederkehrenden Zeiten — täglich um 20 Uhr, wenn die Sicherung anläuft, oder morgens um 8, wenn hundert virtuelle Desktops booten. Zweiter Klassiker: Auto-Tiering, das selten genutzte Blöcke auf langsame Schichten verschiebt — und damit gern die Datendateien erwischt, deren Zugriffe der Buffer Pool tagsüber wegcacht. Die Verhandlungsbasis sind deine Delta-Messungen mit Zeitstempeln: Latenz-Wellen, die sich mit fremden Lastfenstern decken, sind ein Beweis, kein Verdacht. Die Forderung dahinter heißt Dienstgüte: dedizierte oder priorisierte LUNs für Log und tempdb.
4. Der Pfad dazwischen — Virtualisierung, HBA, Multipathing
Zwischen SQL Server und Spindel liegen heute oft fünf Schichten: Dateisystem, Hypervisor, virtueller Controller, HBA, SAN-Fabric. Jede kann Latenz addieren — und die Diagnose-Signatur ist die Lücke zwischen den Messpunkten: SQL sieht 30 ms, das Storage-Array intern 2 ms? Dann verlieren die Schichten dazwischen 28 ms. Übliche Verdächtige: zu kleine Queue-Depth am virtuellen Controller oder HBA, ein falsch konfiguriertes Multipathing (eine von vier Leitungen trägt alles), Snapshot-Ketten unter der VM oder ein Virenscanner, der Datenbankdateien in Echtzeit prüft — Letzteres ist mit den korrekten Ausnahmen für .mdf, .ndf und .ldf in fünf Minuten behoben und erschreckend häufig.
5. Layout-Sünden — und Compression als ehrlicher Tausch
Selbst gemachtes Leid: alles auf einem Volume — Daten, Log, tempdb und Backups teilen sich eine Warteschlange, und die Backup-Stunde verwüstet die Log-Latenz. Die klassische Trennung (Daten / Log / tempdb auf eigene Volumes mit passender Charakteristik) ist keine Folklore, sondern Warteschlangen-Hygiene. Und wenn das Storage am Limit läuft und kurzfristig nichts Schnelleres zu haben ist, gibt es den ehrlichen Tausch: Data Compression (PAGE für historische, leselastige Tabellen) reduziert die zu lesenden Seiten oft um die Hälfte — bezahlt mit CPU, die auf I/O-gebundenen Systemen meist brachliegt. Vorher mit sp_estimate_data_compression_savings schätzen, gezielt komprimieren, Latenz-Delta nachmessen.
Was du auf keinen Fall tun solltest
|
Die drei häufigsten Anfängerfehler 1. Die kumulative DMV ohne Delta lesen — und auf einem Server mit 200 Tagen Uptime „unauffällige“ Durchschnittswerte finden, während das Log seit gestern bei 40 ms liegt. |
|---|
Nachsorge: Damit es nicht wiederkommt
Drei strukturelle Maßnahmen nach dem akuten Fall: Erstens, die Latenz-Baseline automatisieren — die Delta-Messung aus diesem Beitrag als Job, Ergebnisse in eine Tabelle, simple Auswertung daneben. Damit wird aus „seit wann ist das so?“ eine Abfrage statt einer Schätzung. Zweitens, Abnahme-Tests für Storage-Änderungen zur Pflicht machen: Vor jeder Migration eine DiskSpd-Referenzmessung auf alt und neu, mit dokumentierten Parametern — die Lead-Szene dieses Beitrags wäre damit am Migrations-Wochenende aufgefallen, nicht am Mittwoch danach. Drittens, Layout-Standards festschreiben: welche Dateiklasse auf welche Storage-Klasse gehört (Log und tempdb auf das Latenz-Beste, Daten je nach Profil, Backups niemals auf dieselben Spindeln) — als Ein-Seiten-Dokument, das bei jeder neuen Instanz und jeder Storage-Beschaffung auf dem Tisch liegt.
Vertiefung in der Buchreihe
I/O-Architektur, Storage-Auswahl und Latenz-Diagnose füllen in der Buchreihe „SQL Server in der Praxis“ eigene Kapitel — alle Bände in der Übersicht auf der SQL-Pillar-Seite unter /sql-server/.
|
Empfohlene Bände Band 1: Performance & Troubleshooting — das Heimatbuch dieses Beitrags: I/O-Pfad und Latenz-Diagnose im Detail, PAGEIOLATCH- und WRITELOG-Interpretation, Compression-Strategien und die Messmethodik von der DMV bis DiskSpd. |
|---|
Verwandte Notfälle im Notarztwagen
I/O-Latenz hat enge Nachbarn — diese Beiträge aus dem Cluster lohnen den Blick:
Drei Wege, wenn das Storage jetzt gerade bremst
Festpreis-Analyse
Die Latenz-Diskussion zwischen DBA-Team und Storage-Team dreht sich seit Wochen im Kreis, und beide Dashboards behaupten das Gegenteil? Dafür gibt es die Festpreis-Analyse: Ich messe Datei-genau und mit Deltas, fahre die Gegenproben auf OS- und Query-Ebene, ordne die Befunde den Verdächtigen zu — Anforderungsseite, Storage, Pfad — und liefere einen Bericht, der beide Teams auf dieselben Zahlen verpflichtet, samt priorisierter Maßnahmen. Zum vereinbarten Festpreis, ohne Stundenzähler.
Akut-Hotline
Wenn die Log-Latenz gerade jetzt jede Buchung lähmt: anrufen. Halte einen Zugang mit VIEW-SERVER-STATE-Rechten bereit und die Antwort auf die Schlüsselfrage — was hat sich an der Storage-Landschaft zuletzt geändert? Migration, neues Tiering, zusätzliche Mitbewohner auf dem SAN? Mit der Delta-Messung aus diesem Beitrag ist die leidende Datei in Minuten identifiziert.
Selbst-Diagnose-Kit
Du willst die Messkette selbst aufbauen? Das Diagnose-Kit bündelt die Skripte aus diesem Beitrag — Delta-Messung, Top-Reads-Analyse, Perfmon-Counter-Liste, DiskSpd-Parametervorlagen für typische SQL-Lastprofile — als ausführbares Paket mit Auswertungshilfe und den Schwellenwert-Tabellen zum Ausdrucken.
|
Ein letzter Tipp Miss heute einmal die Schreib-Latenz deines Transaktionslogs — fünf Minuten Delta, eine Zahl. Wenn dort auf Flash-Storage mehr als fünf Millisekunden stehen, bezahlt jede einzelne Transaktion deines Unternehmens gerade einen stillen Aufpreis, von dem niemand weiß. Und wenn dort zwei stehen: Glückwunsch, schreib die Zahl auf — das ist deine Baseline für den Tag, an dem das nächste „dreimal so schnelle“ SAN kommt. |
|---|
Häufige Fragen (FAQ)
Diese Sektion ist auf häufige Suchanfragen abgestimmt. Sie hilft dir, schnell den Punkt zu finden, der dir gerade akut weiterhilft — und sie ist gleichzeitig die Eingabe für das FAQPage-Schema auf der Seite.
Wie messe ich I/O-Latenzen im SQL Server?
Aus SQL-Sicht über sys.dm_io_virtual_file_stats: Die DMV führt pro Datenbankdatei Buch über Lese- und Schreibvorgänge samt aufgelaufener Wartezeit (io_stall) — daraus ergibt sich die Durchschnittslatenz je Datei. Zwei Dinge beachten: Die Werte sind kumulativ seit dem letzten Dienststart, also misst du für die Akut-Diagnose immer ein Delta zwischen zwei Snapshots, sonst versteckt sich das Problem von heute im Durchschnitt von Monaten. Und: Die DMV zeigt die Latenz, die der SQL Server erlebt — inklusive aller Schichten dazwischen. Das fertige Delta-Skript steht im Diagnose-Abschnitt dieses Beitrags.
Welche I/O-Latenz ist akzeptabel?
Als pragmatische Richtwerte für Durchschnittslatenzen: Datendateien unter 20 ms sind in Ordnung, 20 bis 50 ms auffällig, über 50 ms ein Problem — auf modernem Flash-Storage solltest du eher einstellige Werte sehen. Das Transaktionslog ist strenger, weil dort synchron geschrieben wird: unter 5 ms gesund, über 10 ms ein echtes Problem, auf SSDs sind 1 bis 2 ms der Normalfall. Wichtig: Das sind Daumenwerte für die Einordnung, kein Naturgesetz — entscheidend ist immer der Vergleich mit der eigenen Baseline und die Frage, ob die Latenz zur Storage-Klasse passt, die ihr bezahlt.
Was bedeutet der Wartetyp PAGEIOLATCH_SH?
PAGEIOLATCH_SH heißt: Eine Session will eine Datenseite lesen, die nicht im Buffer Pool liegt, und wartet darauf, dass sie von der Platte geladen wird — das SH steht für den Shared-Latch, den der Lesevorgang dafür hält. Vereinzelt ist das völlig normal; dominiert der Typ die Wait Stats, liest der Server zu viel von der Platte. Die entscheidende Folgefrage: Liest er zu viel, weil das Storage zu langsam antwortet — oder weil Scans und ein zu kleiner Buffer Pool unnötig viele Seiten anfordern? Genau diese Verzweigung ist der Kern dieses Beitrags.
Wie nutze ich sys.dm_io_virtual_file_stats?
Die Funktion nimmt Datenbank-ID und Datei-ID als Parameter (NULL, NULL für alles) und liefert pro Datei die Anzahl der Reads und Writes, die übertragenen Bytes und die io_stall-Werte — die Gesamtwartezeit auf I/O. Die Durchschnittslatenz rechnest du als io_stall_read_ms geteilt durch num_of_reads (analog für Writes). Verknüpft mit sys.master_files bekommst du Dateinamen und -typ dazu. Der wichtigste Handgriff ist die Delta-Messung: Snapshot in eine Temp-Tabelle, ein paar Minuten Last abwarten, zweiten Snapshot ziehen und die Differenzen berechnen — so misst du die Gegenwart statt der Geschichte.
Sollte ich tempdb auf SSD legen?
In fast allen Fällen: ja, und zwar mit Priorität. Tempdb hat das I/O-Profil, das Flash am meisten belohnt — viele kleine, zufällige Zugriffe, ständiges Schreiben und Lesen durch Sortierungen, Spills und den Version Store, und jede Verzögerung trifft sofort alle Datenbanken der Instanz. Auf moderner Hardware ist lokales NVMe für tempdb sogar im Cluster unkritisch, weil tempdb bei jedem Start ohnehin neu erstellt wird — es muss nicht repliziert oder gesichert werden. Wenn das Budget nur für ein einziges SSD-Upgrade reicht: tempdb und Transaktionslogs zuerst, die Datendateien danach.
Was bringt Data Compression bei I/O-Engpässen?
Data Compression (ROW oder PAGE) ist ein Tauschgeschäft: Du investierst CPU-Zyklen für das Komprimieren und Dekomprimieren — und bekommst dafür kleinere Tabellen und Indizes, also weniger zu lesende Seiten, weniger I/O und nebenbei einen Buffer Pool, in den mehr Daten passen. Auf I/O-gebundenen Systemen mit CPU-Reserven ist das oft ein spürbarer Gewinn, gerade bei Scan-lastigen Workloads und großen historischen Tabellen. Auf CPU-gebundenen Systemen verschiebst du den Engpass dagegen nur. Vorher mit sp_estimate_data_compression_savings das Einsparpotenzial je Tabelle schätzen und gezielt komprimieren statt pauschal.
Wie diagnostiziere ich Storage-Latenz auf dem Betriebssystem?
Mit zwei Werkzeugen. Erstens Perfmon: Die Counter „Avg. Disk sec/Read“ und „Avg. Disk sec/Write“ je Volume zeigen die Latenz aus OS-Sicht — decken sie sich mit den SQL-Werten, liegt das Problem unterhalb von SQL Server; sind sie deutlich besser, frisst eine Schicht dazwischen die Zeit. Zweitens DiskSpd, Microsofts freies Lasttest-Werkzeug: Damit misst du die Roh-Leistung eines Volumes mit definierten Mustern (Blockgröße, Random/Sequential, Queue-Tiefe) — unabhängig vom SQL Server und damit die perfekte gemeinsame Sprache fürs Gespräch mit dem Storage-Team. Vorsicht: DiskSpd-Schreibtests nie auf Volumes mit Produktionsdaten.
