Linux Performance-Analyse und Tuning
Systematisch Performance-Probleme unter Linux finden: vmstat, iostat, sar, iotop, Swap-Verhalten, OOM-Killer und sysctl-Tuning im Praxiseinsatz.
Warum Performance-Analyse eine Methode braucht
“Der Server ist langsam” ist die Beschwerde, mit der praktisch jede Performance-Untersuchung beginnt – und die am wenigsten Informationsgehalt hat. Bevor du irgendwo an Reglern drehst, brauchst du Zahlen: Ist die CPU das Problem, der Arbeitsspeicher, die Platten-I/O oder das Netzwerk? Wer ohne Messung tunt, verschiebt Symptome und produziert im schlimmsten Fall neue Probleme (zum Beispiel einen OOM-Kill, weil vm.swappiness blind auf 0 gesetzt wurde).
Die klassische Methode dafür heisst USE-Methode (Utilization, Saturation, Errors), populär gemacht von Brendan Gregg: Für jede Ressource (CPU, Memory, Disk, Netzwerk) prüfst du Auslastung, Sättigung (Warteschlangen) und Fehler. Dieser Artikel arbeitet sich entlang dieser Ressourcen durch die Standard-Werkzeuge von procps, sysstat und iotop, erklärt was die Zahlen wirklich bedeuten – und zeigt, wie du über sysctl gezielt eingreifst, ohne dir den OOM-Killer ins Haus zu holen.
CPU: Load Average ist nicht Auslastung
Der häufigste Denkfehler in der Linux-Praxis: Load Average mit CPU-Auslastung gleichzusetzen. Das sind zwei völlig verschiedene Dinge.
uptime oder w zeigen drei Zahlen – den Load Average über 1, 5 und 15 Minuten:
$ uptime
14:32:07 up 41 days, 3:12, 2 users, load average: 8.42, 6.15, 4.03
Load Average zählt die Anzahl Prozesse im Zustand R (running/runnable, wartet auf CPU) plus Prozesse im Zustand D (uninterruptible sleep, meist wartend auf I/O). Das ist der entscheidende Punkt: Ein Load Average von 8 auf einer 4-Kern-Maschine kann heissen, dass die CPU überlastet ist – es kann aber genauso gut heissen, dass acht Prozesse auf eine lahme Festplatte oder ein hängendes NFS-Mount warten, während die CPU fast im Leerlauf ist.
vmstat: der Schnelleinstieg
vmstat liefert in einer Zeile pro Intervall die wichtigsten System-Kennzahlen – CPU, Memory, Swap und I/O gemeinsam. Damit fängst du jede Analyse an:
$ vmstat 2 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
3 1 204800 512340 98120 3941200 0 0 12 45 980 1500 22 4 71 3 0
6 2 204800 498120 98120 3942100 0 0 0 890 1120 1800 18 12 40 30 0
1 0 204800 501200 98120 3943000 0 0 0 20 950 1400 15 3 82 0 0
Die Spalten im Detail:
| Spalte | Bedeutung |
|---|---|
r | Prozesse in der Run-Queue (warten auf CPU). Faustregel: dauerhaft > Anzahl Kerne = CPU-Engpass |
b | Prozesse in “uninterruptible sleep” (D), meist I/O-Wartezeit |
swpd | Belegter Swap-Speicher in KB |
free | Freier Speicher in KB |
buff / cache | Puffer und Page-Cache (nicht “verlorener” Speicher, wird bei Bedarf freigegeben) |
si / so | Swap-in / Swap-out in KB/s – kritischer Indikator, sollte im Normalbetrieb 0 sein |
bi / bo | Blocks in/out (Disk-I/O) in KB/s |
in | Interrupts pro Sekunde |
cs | Kontextwechsel pro Sekunde |
us | CPU-Zeit im Userspace |
sy | CPU-Zeit im Kernel (Systemcalls) |
id | CPU im Leerlauf |
wa | CPU wartet auf I/O (I/O-Wait) |
st | ”Stolen” – von Hypervisor entzogene CPU-Zeit (relevant in VMs!) |
In der zweiten Zeile des Beispiels siehst du das Muster für einen I/O-Engpass: wa springt auf 30 %, bo (Block-Output) schnellt auf 890, gleichzeitig sinkt id. Die CPU selbst ist nicht ausgelastet (us+sy = 30 %), sie wartet nur.
mpstat und sar für den Pro-Kern-Blick
vmstat aggregiert über alle Kerne. Bei Multi-Core-Systemen verschleiert das oft einzelne überlastete Kerne (z. B. bei Single-Thread-Anwendungen oder Interrupt-Verteilung). mpstat -P ALL 2 zeigt jeden Kern einzeln:
$ mpstat -P ALL 2 1
Linux 6.8.0-45-generic (webserver01) 07/07/2026 _x86_64_ (8 CPU)
14:40:01 CPU %usr %nice %sys %iowait %irq %soft %steal %idle
14:40:03 all 12.30 0.00 3.10 0.50 0.00 0.20 0.00 83.90
14:40:03 0 88.00 0.00 5.00 0.00 0.00 0.50 0.00 6.50
14:40:03 1 2.10 0.00 1.00 0.10 0.00 0.10 0.00 96.70
...
Hier ist der Gesamtschnitt unauffällig (12 % usr), aber Core 0 läuft bei 88 % – ein klassisches Zeichen für eine single-threaded Anwendung oder unbalancierte Interrupt-Zuweisung (/proc/interrupts prüfen, ggf. irqbalance).
sar (System Activity Reporter) kann dieselben Daten zusätzlich historisch aufzeichnen, wenn der sysstat-Cronjob/Timer aktiv ist:
# Aktivierung der Datensammlung (Debian/Ubuntu)
sudo sed -i 's/ENABLED="false"/ENABLED="true"/' /etc/default/sysstat
sudo systemctl enable --now sysstat
# CPU-Auslastung der letzten Stunde in 10-Minuten-Schritten
sar -u -s 13:00:00 -e 14:00:00
# Live-Sampling wie vmstat, aber mit sar-Syntax: Intervall 2s, 5 Durchläufe
sar -u 2 5
Der grosse Vorteil von sar: Standardmässig werden Daten in /var/log/sysstat/ (RHEL: /var/log/sa/) für die letzten Wochen aufbewahrt. Wenn ein Kunde am Montagmorgen meldet “Freitagnachmittag um 16 Uhr war alles zäh”, kannst du mit sar -u -f /var/log/sysstat/sa05 rückwirkend nachschauen – ganz ohne Monitoring-Tool im Hintergrund mitlaufen zu lassen.
I/O-Bottlenecks mit iostat und iotop finden
Zeigt vmstat erhöhte wa-Werte, brauchst du als Nächstes iostat, um zu sehen, welches Blockdevice der Flaschenhals ist:
$ iostat -xz 2 3
Linux 6.8.0-45-generic (dbserver01) 07/07/2026 _x86_64_ (8 CPU)
Device r/s w/s rkB/s wkB/s await r_await w_await aqu-sz %util
sda 2.50 180.00 120.0 9800.0 45.20 3.10 46.80 8.90 92.50
nvme0n1 0.10 0.50 4.0 16.0 0.30 0.20 0.40 0.00 0.80
Wichtige Spalten:
| Spalte | Bedeutung |
|---|---|
%util | Anteil der Zeit, in der das Device mindestens eine I/O-Anfrage bearbeitet hat. Nahe 100 % = Device ist der Flaschenhals |
await | Durchschnittliche Wartezeit pro I/O-Request in ms (inkl. Warteschlange) |
r_await / w_await | Getrennt nach Lese- und Schreib-Latenz – hilft bei asymmetrischen Problemen (z. B. langsames Sync-Write bei Datenbanken) |
aqu-sz | Durchschnittliche Warteschlangenlänge (Queue Depth) |
rkB/s / wkB/s | Durchsatz in KB/s |
Im Beispiel oben ist sda bei 92 % Auslastung mit w_await von 46 ms – für eine klassische HDD ist das noch im normalen Bereich, für eine SSD/NVMe wäre das bereits alarmierend hoch (dort erwartest du eher unter 1 ms). Der Vergleich mit nvme0n1 in derselben Ausgabe zeigt sofort den Unterschied zwischen den Storage-Klassen.
iotop: welcher Prozess verursacht die I/O-Last
iostat zeigt dir das Device, aber nicht den Verursacher. Dafür gibt es iotop, das analog zu top pro Prozess die Disk-I/O anzeigt:
sudo iotop -oPa
Die wichtigsten Optionen:
| Option | Wirkung |
|---|---|
-o | Nur Prozesse anzeigen, die tatsächlich I/O verursachen (statt aller) |
-P | Nur Prozesse, keine Threads einzeln auflisten (übersichtlicher) |
-a | Akkumulierte I/O seit Start statt Momentanwert – findet “Dauerverursacher” zuverlässiger |
-b | Batch-Modus für Logging/Skripte (nicht-interaktiv) |
-d N | Aktualisierungsintervall in Sekunden |
Praxisbeispiel: Ein Backup-Job (rsync oder restic) läuft tagsüber und bremst die Datenbank aus. iotop -oPa zeigt sofort, welcher Prozess mit welcher PID wie viele MB/s schreibt – Grundlage, um den Job entweder zu terminieren, per ionice zu drosseln oder zeitlich zu verschieben.
# Backup-Prozess auf "Best-Effort, niedrigste Priorität" drosseln,
# ohne ihn zu killen
ionice -c2 -n7 -p <PID>
# Cronjob direkt mit reduzierter I/O- und CPU-Priorität starten
nice -n 19 ionice -c2 -n7 /usr/local/bin/backup.sh
Wenn iotop nicht verfügbar ist (manche gehärteten Container-Basis-Images) oder du nur einen groben Blick brauchst, hilft pidstat -d 2 aus dem sysstat-Paket als Alternative – liefert pro Prozess kB_rd/s, kB_wr/s und ist scriptbarer als iotop.
Speicher, Swap und der OOM-Killer
Wie der Kernel Speicher wirklich verwaltet
free -h ist der Einstieg, wird aber häufig falsch gelesen:
$ free -h
total used free shared buff/cache available
Mem: 31Gi 18Gi 1.2Gi 412Mi 12Gi 13Gi
Swap: 8.0Gi 1.1Gi 6.9Gi
Der Wert free (1,2 GiB) sagt fast nichts aus – Linux nutzt freien Speicher aktiv als Page-Cache (buff/cache), um Festplattenzugriffe zu vermeiden. Das ist gewollt und wird bei Bedarf sofort freigegeben. Die relevante Spalte ist available: eine Schätzung, wie viel Speicher tatsächlich für neue Prozesse verfügbar wäre, ohne zu swappen.
Dass Swap bei 1,1 GiB nicht leer ist, ist noch kein Alarmsignal – der Kernel lagert auch bei genug freiem RAM gelegentlich selten genutzte (kalte) Speicherseiten aus, um Platz für Cache zu schaffen. Kritisch wird es erst, wenn vmstat laufend aktive si/so-Werte (Swap-in/-out) zeigt – das bedeutet aktives Nachladen unter Zeitdruck, spürbar als Latenz-Spitzen.
vm.swappiness verstehen und richtig setzen
vm.swappiness ist kein Schalter “ab wie viel % RAM wird geswappt”, sondern ein Gewichtungsfaktor zwischen 0 und 200 (klassisch dokumentiert 0–100), der bestimmt, wie stark der Kernel beim Speicher-Reclaiming anonyme Seiten (Prozessspeicher, potenziell swap-fähig) gegenüber Datei-Cache-Seiten bevorzugt zurückzuholen.
# Aktuellen Wert anzeigen
cat /proc/sys/vm/swappiness
# Temporär setzen (bis zum nächsten Reboot)
sudo sysctl vm.swappiness=10
# Persistent setzen
echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swappiness.conf
sudo sysctl --system
| Swappiness | Verhalten | Typischer Einsatz |
|---|---|---|
| 0 | Kernel vermeidet Swap so lange wie möglich, swappt erst wenn Free+File-Cache unter High-Watermark fallen | Latenzkritische Workloads (Redis, In-Memory-Caches) auf Systemen mit reichlich RAM |
| 1–10 | Sehr zurückhaltend, swappt nur unter echtem Druck | Datenbankserver (PostgreSQL, MySQL), die eigenes Memory-Management betreiben |
| 60 (Default) | Ausgewogen zwischen Anwendungsspeicher und Cache | Generische Server, Desktops |
| 80–100 | Kernel swappt aktiver zugunsten von Datei-Cache | Fileserver mit viel sequenziellem Lesezugriff, wenig aktiven Prozessen |
Der OOM-Killer: letzte Verteidigungslinie
Wenn der Kernel trotz Swap keinen Speicher mehr freibekommt, greift der Out-of-Memory-Killer: Er berechnet für jeden Prozess einen oom_score und beendet den mit dem höchsten Wert, um das Gesamtsystem am Leben zu erhalten. Das ist kein Bug, sondern Design – die Alternative wäre ein komplett hängendes System.
Nachvollziehen kannst du einen OOM-Kill im Log:
dmesg -T | grep -i "out of memory"
journalctl -k | grep -i "killed process"
Eine typische Zeile sieht so aus: Out of memory: Killed process 4821 (java) total-vm:8241232kB, anon-rss:6291456kB, ... oom_score_adj:0.
Den oom_score eines Prozesses siehst du direkt:
cat /proc/<PID>/oom_score
cat /proc/<PID>/oom_score_adj
oom_score_adj (Bereich -1000 bis +1000) erlaubt dir, gezielt einzugreifen: Ein Wert von -1000 macht einen Prozess für den OOM-Killer komplett unantastbar (nutze das mit Bedacht – z. B. für sshd, damit du im Notfall noch draufkommst), ein positiver Wert macht ihn zum bevorzugten Ziel.
# sshd vor dem OOM-Killer schuetzen, damit Fernzugriff im Notfall erhalten bleibt
echo -1000 > /proc/$(pgrep -x sshd | head -1)/oom_score_adj
# Einen bekannten Speicherfresser (z.B. Batch-Job) zum bevorzugten Opfer machen
echo 500 > /proc/<PID>/oom_score_adj
Für Dienste unter systemd lässt sich der OOM-Schutz sauber deklarativ statt mit manuellen /proc-Schreibzugriffen lösen:
# /etc/systemd/system/myapp.service.d/override.conf
[Service]
OOMScoreAdjust=-500
MemoryMax=4G
MemoryHigh=3.5G
MemoryMax erzwingt ein hartes cgroup-Limit (der Prozess wird bei Überschreitung vom cgroup-OOM-Killer beendet, nicht vom globalen), MemoryHigh bremst den Prozess vorher durch Reclaim-Druck – oft die sanftere und vorhersehbarere Variante im Vergleich zum globalen OOM-Killer.
Kernel-Parameter mit sysctl gezielt tunen
sysctl liest und schreibt Kernel-Parameter zur Laufzeit unter /proc/sys/. Der Workflow für produktive Änderungen:
# 1. Aktuellen Wert pruefen
sysctl vm.swappiness
# 2. Live testen (Wirkung sofort, aber nicht persistent)
sudo sysctl -w vm.swappiness=10
# 3. Verhalten unter Last beobachten (vmstat, iostat wie oben)
# 4. Wenn zufrieden: persistent in eigener Datei ablegen
echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-tuning.conf
# 5. Alle Dateien in /etc/sysctl.d/ neu einlesen und Konsistenz pruefen
sudo sysctl --system
Die wichtigsten Parameter für Performance-Tuning im Überblick:
| Parameter | Default | Wirkung | Typischer Tuning-Anlass |
|---|---|---|---|
vm.swappiness | 60 | Swap- vs. Cache-Reclaim-Gewichtung | Datenbank-/Latenz-Workloads senken auf 1–10 |
vm.min_free_kbytes | Auto (RAM-abhängig) | Minimal freizuhaltender Speicher pro Zone | Erhöhen gegen OOM-Kills unter Lastspitzen |
vm.dirty_ratio | 20 | % RAM, ab dem Prozesse selbst blockierend flushen müssen | Senken bei Storage mit hoher Latenz, um I/O-Stösse zu glätten |
vm.dirty_background_ratio | 10 | % RAM, ab dem Kernel-Threads asynchron im Hintergrund flushen | An Storage-Durchsatz anpassen |
vm.overcommit_memory | 0 (heuristisch) | Ob/wie Speicher-Überbuchung erlaubt ist | 2 (strikt) für Systeme, wo OOM-Kills inakzeptabel sind |
net.core.somaxconn | 4096 (aktuelle Kernel) | Max. Backlog für eingehende TCP-Verbindungen | Erhöhen bei vielen kurzlebigen Verbindungen (Webserver, Load Balancer) |
net.ipv4.tcp_fin_timeout | 60 | Wartezeit im FIN-WAIT-2-Zustand | Senken bei sehr hoher Connection-Churn-Rate |
fs.file-max | Auto (RAM-abhängig) | Systemweites Limit offener Dateideskriptoren | Erhöhen bei Datenbanken, Java-Anwendungen mit vielen Handles |
Entscheidungsablauf für eine Performance-Untersuchung
Ein pragmatischer Ablauf für den IT-Alltag im KMU, wenn “der Server ist langsam” gemeldet wird:
uptime/w– Load Average grob einordnen (im Verhältnis zur Kernanzahl).vmstat 2 10–r,b,wa,si/sobeobachten: CPU-, I/O- oder Swap-Problem?- Bei hohem
us/sy:top/mpstat -P ALL– welcher Prozess, welcher Kern? - Bei hohem
wa:iostat -xz 2– welches Device ist gesättigt (%util,await)? - Bei gesättigtem Device:
iotop -oPa– welcher Prozess verursacht die I/O? - Bei aktivem
si/so:free -h+ps aux --sort=-%mem– welcher Prozess frisst Speicher, ist es ein Leak? - Nach OOM-Kills suchen:
journalctl -k | grep -i killed– wurde bereits etwas beendet? - Historischen Kontext holen:
sar -u -f /var/log/sysstat/saXX– war das Muster schon vorher da oder neu? - Erst danach gezielt an
sysctl-Parametern drehen, mit Vorher-/Nachher-Messung.
Dieser Ablauf verhindert das häufigste Anti-Pattern: sofort an swappiness oder dirty_ratio zu drehen, bevor überhaupt klar ist, welche Ressource wirklich der Engpass ist.
Weiterlernen
- Kernel-Doku: sysctl vm-Parameter (docs.kernel.org)
- Red Hat Performance Tuning Guide
- sysstat-Projekt (iostat, sar, mpstat, pidstat)
- Brendan Gregg: Linux Performance (USE-Methode, Tools-Übersicht)
- man7.org: proc(5) – /proc/sys/vm Dokumentation
- Kernel-Doku: OOM-Killer (docs.kernel.org)
Verwandte Themen: Linux-Grundbefehle, systemd-Dienste verwalten, Linux-Hardening und Security, Monitoring-Grundlagen, Virtualisierung-Grundlagen
Kommentare
Frage, Verbesserungsvorschlag oder eigene Erfahrung zu diesem Artikel? Schreib einen Kommentar. Neue Beiträge erscheinen nach kurzer Moderation.
- Lade Kommentare …