Vom Symptom zum verursachenden Prozess
Hohe Systemlast ist nicht automatisch ein Zeichen für einen langsamen Datenträger. Dieser Leitfaden zeigt, wie iostat mit Intervall-Sampling echte Trends von kurzen Lastspitzen unterscheidet, wie iotop den konkreten Prozess hinter hoher I/O-Auslastung identifiziert, und wie Kennzahlen wie %util und await zuverlässig zeigen, ob ein Disk-Problem oder ein Anwendungsproblem vorliegt.
Inhaltsverzeichnis
- 1. Wann Disk-I/O wirklich der Flaschenhals ist
- 2. iostat Grundlagen: %util, await und svctm verstehen
- 3. Intervall-Sampling mit iostat für Trend-Erkennung
- 4. iotop: den verursachenden Prozess identifizieren
- 5. %util und await richtig interpretieren
- 6. Slow-Disk-Problem von Slow-Application-Problem unterscheiden
- 7. Praxisbeispiel: MySQL- und Magento-I/O-Diagnose
- 8. Ergänzende Werkzeuge: pidstat, /proc/diskstats und blktrace
- 9. iostat und iotop im Werkzeugvergleich
- 10. Zusammenfassung
- 11. FAQ
1. Wann Disk-I/O wirklich der Flaschenhals ist
Ein klassisches Fehlbild in der Praxis: Ein Shop reagiert träge, das Team schaut in htop, sieht moderate CPU-Last und schließt daraus, dass mehr CPU-Kerne das Problem lösen. Tatsächlich hängen die Prozesse aber im Zustand D (uninterruptible sleep) fest, weil sie auf Antworten vom Datenträger warten. CPU-Last allein sagt nichts darüber aus, ob ein System Disk-I/O-gebunden ist, denn wartende Prozesse verbrauchen kaum Rechenzeit. Das richtige Signal liefert die %wa-Spalte in top oder vmstat, die den Anteil der CPU-Zeit zeigt, in der der Prozessor untätig auf ausstehende I/O-Operationen wartet.
Ist %wa über längere Zeit deutlich erhöht, etwa dauerhaft über 20 Prozent, lohnt sich der nächste Schritt: iostat und iotop geben Auskunft darüber, welcher Datenträger wie stark ausgelastet ist und welcher Prozess die Anfragen erzeugt. Ohne diese beiden Werkzeuge bleibt jede Diagnose Spekulation, denn weder htop noch free zeigen die tatsächliche Warteschlangentiefe oder die Latenz einzelner I/O-Anfragen. Die folgenden Abschnitte zeigen, wie man mit konkreten Kennzahlen zwischen einem echten Hardware-Engpass und einer ineffizienten Anwendung unterscheidet, bevor teure Infrastruktur-Entscheidungen getroffen werden.
2. iostat Grundlagen: %util, await und svctm verstehen
iostat ist Teil des sysstat-Pakets und liest seine Daten direkt aus /proc/diskstats. Im erweiterten Modus mit -x liefert es pro Gerät Kennzahlen wie r/s und w/s (Lese- und Schreiboperationen pro Sekunde), rkB/s und wkB/s (Durchsatz), await (durchschnittliche Wartezeit einer I/O-Anfrage in Millisekunden, inklusive Warteschlange), avgqu-sz (durchschnittliche Länge der Warteschlange) und %util (Anteil der Zeit, in der das Gerät mindestens eine ausstehende Anfrage hatte). Die Option -z blendet inaktive Geräte aus und hält die Ausgabe übersichtlich.
Ein häufiger Stolperstein: Der allererste Report von iostat zeigt den Durchschnitt seit dem letzten Systemstart, nicht den aktuellen Zustand. Für eine belastbare Momentaufnahme muss man diesen ersten Block ignorieren und erst den zweiten oder folgende Reports auswerten. Die Spalte svctm gilt seit Kernel-Versionen mit modernen I/O-Schedulern als unzuverlässig und wurde in aktuellen sysstat-Versionen entfernt, weshalb await zusammen mit avgqu-sz heute die aussagekräftigeren Metriken für Latenz und Sättigung sind.
# Install sysstat if iostat/iotop-related tooling is missing (Debian/Ubuntu)
apt-get install -y sysstat
# Extended stats, skip inactive devices, refresh every second
iostat -xz 1
# Example output (second report, first block already discarded)
# Device r/s w/s rkB/s wkB/s await avgqu-sz %util
# nvme0n1 4.00 312.00 64.00 9840.00 38.42 6.10 97.50
# sda 0.00 2.00 0.00 24.00 1.80 0.01 0.40
3. Intervall-Sampling mit iostat für Trend-Erkennung
Eine einzelne Momentaufnahme mit iostat -x 1 1 ist für eine seriöse Diagnose fast wertlos, weil sie nur eine zufällige Sekunde abbildet. Kurzzeitige Lastspitzen, etwa durch einen einzelnen Backup-Job oder einen Cron-Lauf, sehen in einer einzigen Momentaufnahme identisch aus wie ein dauerhaftes strukturelles Problem. Erst kontinuierliches Sampling über mehrere Minuten oder Stunden zeigt, ob %util und await tatsächlich konstant hoch bleiben oder nur kurz ausschlagen und sich danach wieder normalisieren.
Für produktive Systeme empfiehlt sich iostat -xz 2 30, also 30 Samples im Abstand von zwei Sekunden, umgeleitet in eine Logdatei zur späteren Auswertung. Wer dauerhafte historische Daten braucht, aktiviert die persistente Sammlung von sysstat über sa1, das automatisch Binärdaten in /var/log/sysstat schreibt und später mit sar -d für beliebige Zeitfenster ausgewertet werden kann, etwa um zu prüfen, ob ein Vorfall vor drei Tagen um 14 Uhr mit einem I/O-Spike zusammenfiel.
4. iotop: den verursachenden Prozess identifizieren
iostat zeigt, welches Gerät ausgelastet ist, aber nicht, welcher Prozess dafür verantwortlich ist. Genau diese Lücke schließt iotop, das über das Kernel-Taskstats-Interface pro Prozess und Thread tatsächliche Lese- und Schreibraten anzeigt. Da dieses Interface über Netlink-Sockets kommuniziert und privilegierte Kernel-Informationen liefert, benötigt iotop zwingend Root-Rechte oder das Capability CAP_NET_ADMIN, ein einfacher Aufruf als normaler Benutzer scheitert mit einer Fehlermeldung.
Die Option -o (nur Prozesse mit tatsächlicher I/O anzeigen) reduziert die Ausgabe auf das Wesentliche, während -a die kumulierte statt der momentanen Rate zeigt und damit auch kurze, aber häufige I/O-Bursts sichtbar macht, die in der Momentaufnahme sonst untergehen. Für Skripte und Cron-basiertes Monitoring eignet sich der Batch-Modus mit -b, der die Ausgabe ohne interaktives Terminal in eine Logdatei schreibt. Die von iotop gemeldete PID lässt sich anschließend mit ps -fp <pid> oder cat /proc/<pid>/cmdline einem konkreten Kommando zuordnen.
# Interactive view, only processes with actual I/O, accumulated totals
iotop -oa
# Batch mode for cron/logging: 5 samples, 2 seconds apart
iotop -b -n 5 -d 2 -o > /var/log/iotop-batch.log
# Resolve the PID iotop reported to the actual command line
ps -fp 18342
# UID PID PPID C STIME TTY TIME CMD
# mysql 18342 1 6 09:14 ? 00:04:12 mysqld --datadir=/var/lib/mysql
5. %util und await richtig interpretieren
%util von 100 Prozent bedeutet nicht zwangsläufig einen Engpass. Bei einer einzelnen rotierenden Festplatte mit nur einem Lese-/Schreibkopf ist 100 Prozent tatsächlich ein Sättigungssignal. Bei modernen NVMe-SSDs mit vielen parallelen Warteschlangen (bis zu 64.000 Queues mit je 64.000 Einträgen laut NVMe-Spezifikation) kann das Gerät hingegen bei 100 Prozent %util weiterhin problemlos zusätzliche parallele Anfragen bedienen, ohne dass die Latenz spürbar steigt. Entscheidend ist deshalb immer die Kombination aus %util und await, niemals eine Kennzahl isoliert.
Auch bei await braucht es einen Referenzwert je Hardware-Typ: Eine gute NVMe-SSD liegt typischerweise bei 1 bis 3 Millisekunden, eine SATA-SSD bei 3 bis 8 Millisekunden, eine rotierende Festplatte bei 5 bis 15 Millisekunden, und Netzwerkspeicher wie iSCSI oder NFS kann je nach Netzwerklatenz deutlich höhere Werte als normal aufweisen. Ohne diesen Baseline-Wert für das jeweilige Setup ist jede Alarmierung willkürlich. Ein steigender avgqu-sz parallel zu steigendem await ist das zuverlässigste Signal für echte Sättigung, weil es zeigt, dass Anfragen sich stauen statt zügig abgearbeitet zu werden.
6. Slow-Disk-Problem von Slow-Application-Problem unterscheiden
Die zentrale Diagnosefrage lautet: Wartet die Anwendung auf einen tatsächlich langsamen Datenträger, oder erzeugt die Anwendung selbst unnötig viele I/O-Anfragen, die auch eine schnelle Disk in die Knie zwingen würden? Beide Fälle zeigen ähnlich hohe %wa-Werte in top, lassen sich aber klar trennen: Bleibt await auch bei moderater Anfragerate hoch, deutet das auf ein echtes Hardware- oder Storage-Layer-Problem hin, etwa einen degradierten RAID-Verbund oder überlastetes Netzwerk-Storage. Steigt hingegen vor allem die Anzahl der Anfragen (r/s/w/s) drastisch, während die einzelne Anfrage selbst noch normal schnell beantwortet wird, liegt das Problem im Anwendungscode, etwa durch fehlende Indizes oder ein zu kleines Caching.
Ein einfacher praktischer Test bestätigt die Diagnose: Wird die vermutete Ursache im Code behoben, etwa eine fehlende Indexierung ergänzt oder ein Query-Cache aktiviert, und sinken %util sowie await danach spürbar, war die Anwendung die Ursache. Bleiben die Werte trotz reduzierter Anfragerate weiterhin hoch, liegt der Flaschenhals tatsächlich im Storage-Layer selbst und muss dort behoben werden, etwa durch schnellere Datenträger oder eine überprüfte RAID-Konfiguration.
{
"check": "disk_io_bottleneck_classification",
"host": "shop-prod-03",
"timestamp": "2026-07-12T14:02:00Z",
"device": "nvme0n1",
"await_ms": 4.20,
"util_percent": 38.00,
"iops_read": 210,
"iops_write": 1840,
"cpu_iowait_percent": 3.10,
"classification": "application_bound",
"note": "High IOPS but low await/util: check application query patterns, not the storage layer"
}
7. Praxisbeispiel: MySQL- und Magento-I/O-Diagnose
Ein typisches Szenario: Ein Magento-Shop wird nach einem Traffic-Anstieg spürbar langsamer, Seitenaufbauten dauern mehrere Sekunden. iostat -xz 2 30 zeigt für das Volume mit /var/lib/mysql einen await-Wert von konstant 45 Millisekunden bei %util nahe 95 Prozent, deutlich über dem Normalwert der eingesetzten SSD. iotop -oa bestätigt: mysqld erzeugt den überwiegenden Teil der Schreiblast, mit ungewöhnlich vielen kleinen, zufälligen Schreibvorgängen statt weniger größerer sequentieller Blöcke.
Der nächste Schritt ist nicht der Austausch der Festplatte, sondern die Prüfung von MySQL selbst: SHOW ENGINE INNODB STATUS und der InnoDB Buffer Pool Hit Ratio zeigen, ob der Buffer Pool (innodb_buffer_pool_size) zu klein für den aktiven Datensatz dimensioniert ist. Ist die Trefferquote niedrig, muss MySQL für fast jede Leseanfrage tatsächlich auf die Disk zugreifen, statt aus dem Speicher zu bedienen, was bei steigendem Traffic genau das beobachtete Muster erzeugt. Eine Vergrößerung des Buffer Pools auf einen realistischen Anteil des verfügbaren RAMs senkt in diesem Fall await und %util deutlich, ohne dass überhaupt neue Hardware angeschafft werden muss.
8. Ergänzende Werkzeuge: pidstat, /proc/diskstats und blktrace
pidstat -d 1 aus demselben sysstat-Paket liefert eine Alternative zu iotop, die sich besser für historisches Logging eignet, weil sie wie iostat reine Textzeilen ausgibt und sich problemlos in eine Datei umleiten oder mit awk weiterverarbeiten lässt. Für eigene Monitoring-Skripte, die ohne externe Tools auskommen sollen, liefert /proc/diskstats dieselben Rohdaten, aus denen iostat seine Kennzahlen berechnet, direkt als Zahlenspalten, allerdings ohne Vorverarbeitung wie Prozentwerte oder gleitende Mittelwerte.
Bleibt die Ursache trotz iostat und iotop unklar, etwa weil die Latenz nicht beim Gerät selbst, sondern irgendwo im Block-Layer-Stack entsteht, hilft blktrace zusammen mit blkparse für eine detaillierte Aufzeichnung jeder einzelnen I/O-Anfrage inklusive Zeitstempeln für Warteschlange, Dispatch und Abschluss. Das ist deutlich aufwendiger auszuwerten als iostat, zeigt aber auch Effekte wie I/O-Merging durch den Scheduler oder ungewöhnlich lange Verweildauern einzelner Anfragen in der Warteschlange, die in aggregierten Kennzahlen verborgen bleiben. Für die dauerhafte historische Sammlung reicht meist die persistente Konfiguration von sysstat.
; /etc/default/sysstat - enable persistent historical sampling on Debian/Ubuntu
ENABLED="true"
; /etc/cron.d/sysstat - default sampling every 10 minutes into /var/log/sysstat
*/10 * * * * root command -v debian-sa1 > /dev/null && debian-sa1 1 1
; Query historical disk stats for a specific window later on
; sar -d -f /var/log/sysstat/sa12 -s 14:00:00 -e 14:30:00
# prometheus alert rule: fire when a device is both saturated and slow
groups:
- name: disk-io
rules:
- alert: DiskIOBound
expr: |
rate(node_disk_io_time_seconds_total[5m]) > 0.9
and
rate(node_disk_write_time_seconds_total[5m])
/ rate(node_disk_writes_completed_total[5m]) > 0.03
for: 5m
labels:
severity: warning
annotations:
summary: "Disk {{ $labels.device }} saturated with high await"
description: "util > 90% and await > 30ms for 5m - likely a real storage bottleneck"
9. iostat und iotop im Werkzeugvergleich
Die folgende Tabelle fasst die wichtigsten Schwellenwerte zusammen, die sich in der Praxis bewährt haben, um zwischen unauffälligem Betrieb und einem echten Problem zu unterscheiden. Wichtig bleibt dabei immer: Ein einzelner Wert außerhalb der Norm ist noch kein Beweis, entscheidend ist das Muster über Zeit und die Kombination mehrerer Kennzahlen.
| Kennzahl | Unauffälliger Wert | Kritischer Wert | Handlungsempfehlung |
|---|---|---|---|
| %util | Unter 70 %, auch bei Lastspitzen | Dauerhaft nahe 100 % | Mit iostat -xz 1 über mehrere Minuten beobachten, nicht nur eine Momentaufnahme |
| await (ms) | Im Bereich der Hardware-Spezifikation | Deutlich über Baseline, z. B. 40 ms+ bei SSD | Baseline je Gerätetyp definieren, bevor Schwellwerte gesetzt werden |
| avgqu-sz | Nahe 0 bis 1 | Kontinuierlich über 4 bis 8 | Zeigt echten Anfragestau, nicht nur einzelne kurze Spitzen |
| iotop-Ausgabe | I/O verteilt auf erwartete Prozesse (mysqld, php-fpm) | Ein einzelner unerwarteter Prozess dominiert | iotop -oa laufen lassen, PID mit ps -fp und lsof abgleichen |
| CPU %wa (iowait) | Unter 5 % | Dauerhaft über 20 % | Klassisches Disk-bound-Symptom, Prozesse warten statt zu rechnen |
| Reaktion nach App-Fix | await/%util sinken nach Query-Optimierung | Werte bleiben hoch trotz reduzierter Anfragen | Storage-Layer selbst ist die Ursache, nicht die Anwendung |
In der Praxis lohnt es sich, diese Schwellenwerte als Prometheus- oder Zabbix-Regeln zu hinterlegen, statt sie nur manuell mit iostat zu prüfen. So fällt eine schleichende Verschlechterung über Wochen auf, lange bevor Nutzer sie über Ladezeiten spüren, und das Team muss nicht erst nach einer Beschwerde reaktiv nach der Ursache suchen.
Mironsoft
Linux-Systemadministration, Performance-Diagnose und DevOps für Magento-Infrastruktur
Langsamer Shop trotz vermeintlich guter Hardware?
Wir analysieren eure Server mit iostat, iotop und pidstat, trennen echte Storage-Engpässe von ineffizientem Anwendungscode und beheben die tatsächliche Ursache statt nur teurere Hardware zu empfehlen.
I/O-Audit
Systematisches Intervall-Sampling mit iostat und iotop auf euren Produktionsservern
MySQL-Tuning
Buffer Pool, Indizes und Query-Muster prüfen, bevor neue Hardware angeschafft wird
Monitoring-Setup
Prometheus- oder Zabbix-Regeln für %util und await einrichten, bevor Nutzer es merken
10. Zusammenfassung
Die Disk-I/O-Analyse mit iostat und iotop folgt einem klaren Ablauf: Zuerst bestätigt %wa in top, dass Prozesse überhaupt auf I/O warten. Danach zeigt iostat -xz mit Intervall-Sampling über mehrere Minuten, ob %util und await tatsächlich konstant erhöht sind oder nur kurz ausschlagen. iotop -oa identifiziert anschließend den konkreten Prozess hinter der Last, während pidstat -d dieselbe Information für historisches Logging liefert. Eine einzelne Momentaufnahme reicht für keine dieser Fragen aus, jede belastbare Diagnose braucht kontinuierliche Beobachtung über Zeit.
Der wichtigste Denkfehler in der Praxis ist die Annahme, hohe I/O-Last bedeute automatisch eine zu langsame Festplatte. Oft ist das Gegenteil richtig: Die Anwendung erzeugt selbst zu viele oder zu ineffiziente Anfragen, etwa durch einen zu kleinen InnoDB Buffer Pool oder fehlende Indizes. Bleiben await und %util nach einer Anwendungsoptimierung weiterhin hoch, liegt der Engpass tatsächlich im Storage-Layer und rechtfertigt eine Hardware-Investition. Diese Reihenfolge, erst Anwendung prüfen, dann Hardware infrage stellen, spart in den meisten Fällen unnötige Kosten.
Disk-I/O-Analyse mit iostat und iotop: Das Wichtigste auf einen Blick
Erstes Signal
%wa in top/vmstat zeigt, ob Prozesse überhaupt auf I/O warten, bevor iostat und iotop überhaupt nötig sind.
Kennzahlen kombinieren
%util allein ist kein Beweis. Erst zusammen mit await und avgqu-sz über Zeit ergibt sich ein klares Bild.
Prozess finden
iotop -oa zeigt den konkreten Prozess hinter der I/O-Last, pidstat -d eignet sich für Logging.
Disk vs. Anwendung
Sinkt await nach einem App-Fix nicht, liegt der Engpass im Storage-Layer, nicht im Code.