Der I/O Scheduler Linux entscheidet, wie das System Lese- und Schreibzugriffe auf SSD, NVMe und HDD sortiert, priorisiert und zum Gerät sendet. In diesem Leitfaden erkläre ich praxisnah, wann Noop, mq-deadline und BFQ im Hosting die beste Wahl sind – inklusive Tuning, Tests und klaren Handlungsschritten.
Zentrale Punkte
- Noop: Minimaler Overhead auf SSD/NVMe und in VMs
- mq-deadline: Ausgewogene Latenz und Durchsatz für Server
- BFQ: Fairness und schnelle Reaktion bei Multi-User
- blk-mq: Multi-Queue-Design für moderne Hardware
- Tuning: Tests je Workload statt fester Regeln
Wie der I/O Scheduler im Linux-Hosting wirkt
Ein Linux-I/O-Scheduler ordnet I/O-Anfragen in Queues, führt Merging durch und entscheidet die Auslieferung zum Gerät, um Latenz zu senken und Durchsatz zu heben. Moderne Kernel nutzen blk-mq, also Multi-Queue, damit mehrere CPU-Kerne parallel I/O anstoßen können. Das passt zu NVMe-SSDs, die viele Queues und hohe Parallelität bieten und so Warteschlangen verkürzen. Im Hosting treffen oft breite Mischlasten aufeinander: Webserver liefern viele kleine Reads, Datenbanken erzeugen Sync-Writes, Backups erzeugen Streams. Der passende Scheduler reduziert Staus, hält Antwortzeiten stabil und schützt die Server-Erfahrung unter Last.
blk-mq in der Praxis: none vs. noop und Kernel-Defaults
Seit Kernel 5.x ist das Multi-Queue-Design der Standardpfad. Dabei ist none der „Noop“-Äquivalent für blk-mq, während noop historisch aus dem Single-Queue-Pfad stammt. Auf NVMe-Geräten ist meist nur none verfügbar; auf SATA/SAS sieht man häufig mq-deadline, optional bfq und je nach Distribution auch kyber. Die Defaults variieren: NVMe startet in aller Regel mit none, SCSI/SATA oft mit mq-deadline. Ich prüfe daher immer die verfügbaren Optionen via cat /sys/block/<dev>/queue/scheduler und entscheide pro Gerät. Wo nur none auswählbar ist, ist das gewollt – zusätzliche Sortierung bringt dort praktisch keinen Mehrwert.
Noop im Servereinsatz: Wann der Minimalismus gewinnt
Noop führt vor allem Merging benachbarter Blöcke durch, sortiert jedoch nicht, was den CPU-Overhead extrem gering hält. Auf SSDs und NVMe übernehmen Controller und Firmware die clevere Reihenfolge, sodass zusätzliche Sortierung im Kernel kaum Nutzen bringt. In VMs und Containern plane ich oft Noop ein, weil der Hypervisor ohnehin übergreifend plant. Auf Rotationsplatten verzichte ich auf Noop, da fehlende Sortierung dort Seek-Zeiten erhöht. Wer den Hardware-Kontext sicher abgrenzen will, schaut zuerst auf den Speicher-Typ – hier hilft ein Blick auf NVMe, SSD und HDD, bevor ich den Scheduler festlege.
mq-deadline: Fristen, Reihenfolgen und klare Prioritäten
mq-deadline gibt Lesezugriffen kurze Deadlines und lässt Schreibzugriffe etwas länger warten, um Antwortzeit spürbar zu sichern. Der Scheduler sortiert zudem nach Block-Adressen und senkt damit Suchzeiten, was vor allem HDDs und RAID-Verbünden hilft. In Web- und Datenbank-Hosts liefert mq-deadline eine gute Balance aus Latenz und Durchsatz. Ich setze ihn gern ein, wenn Workloads gemischt sind und sowohl Reads als auch Writes dauerhaft anstehen. Für das Feintuning prüfe ich Request-Tiefe, Writeback-Verhalten und Controller-Cache, damit die Deadline-Logik konsistent greift.
BFQ: Fairness und Reaktionsfreude für viele gleichzeitige Nutzer
BFQ verteilt die Bandbreite proportional und vergibt Budgets pro Prozess, was spürbar fair wirkt, wenn viele User parallel I/O erzeugen. Interaktive Tasks wie Admin-Shells, Editoren oder API-Calls bleiben flott, obwohl im Hintergrund Backups laufen. Auf HDDs erreicht BFQ oft hohe Effizienz, weil es sequentielle Phasen ausnutzt und kurze Idle-Fenster klug einsetzt. Auf sehr schnellen SSDs entsteht ein wenig Zusatzaufwand, den ich gegen die spürbare Reaktionsfreude abwäge. Wer Cgroups und ioprio nutzt, kann mit BFQ klare Zusicherungen herstellen und so Ärger durch laute Nachbarn vermeiden.
QoS im Alltag: ioprio, ionice und Cgroups v2 mit BFQ
Für saubere Priorisierung kombiniere ich BFQ mit Prozess- und Cgroup-Regeln. Auf Prozessebene setze ich mit ionice Klassen und Prioritäten: ionice -c1 (Realtime) für Latenz-kritische Reads, ionice -c2 -n7 (Best-Effort, niedrig) für Backups oder Index-Läufe, ionice -c3 (Idle) für alles, das nur in Leerlaufzeiten laufen soll. In Cgroups v2 nutze ich io.weight für relative Anteile (z. B. 100 vs. 1000) und io.max für harte Limits, etwa echo "259:0 rbps=50M wbps=20M" > /sys/fs/cgroup/<grp>/io.max. Mit BFQ werden Gewichte sehr präzise in Bandbreitenanteile umgesetzt – ideal für Shared-Hosting und Container-Hosts, auf denen Fairness wichtiger ist als maximale Rohleistung.
Praxisvergleich: Welche Wahl zur Hardware passt
Die Wahl hängt stark vom Speicher-Typ und der Queue-Architektur ab, daher prüfe ich zuerst Gerät und Controller. SSD und NVMe profitieren meist von Noop/none, HDDs laufen mit mq-deadline oder BFQ runder. In RAID-Setups, SANs und Allround-Hosts ziehe ich mq-deadline häufig vor, weil Deadline-Logik und Sortierung gut harmonieren. Multi-User-Umgebungen mit vielen interaktiven Sessions gewinnen oft durch BFQ. Die folgende Tabelle fasst die Stärken und sinnvollen Einsatzfelder übersichtlich zusammen:
| Scheduler | Hardware | Stärken | Schwächen | Hosting-Szenarien |
|---|---|---|---|---|
| Noop/none | SSD, NVMe, VMs | Minimaler Overhead, sauberes Merging | Ohne Sortierung auf HDDs nachteilig | Flash-Server, Container, Hypervisor-gesteuert |
| mq-deadline | HDD, RAID, Allround-Server | Strenge Read-Priorität, Sortierung, solide Latenz | Mehr Logik als Noop | Datenbanken, Web-Backends, gemischte Lasten |
| BFQ | HDD, Multi-User, Desktop-ähnliche Hosts | Fairness, Reaktionsfreude, gute Sequenzen | Etwas mehr Overhead auf sehr schnellen SSDs | Interaktive Dienste, Shared-Hosting, Dev-Server |
Konfiguration: Scheduler prüfen und dauerhaft setzen
Zuerst schaue ich, welcher Scheduler aktiv ist, etwa mit cat /sys/block/sdX/queue/scheduler, und notiere die Option in eckigen Klammern. Um temporär zu wechseln, schreibe ich zum Beispiel echo mq-deadline | sudo tee /sys/block/sdX/queue/scheduler. Für persistente Einstellungen nutze ich udev-Regeln oder Kernel-Parameter wie scsi_mod.use_blk_mq=1 und mq-deadline in der Commandline. Bei NVMe-Geräten prüfe ich Pfade unter /sys/block/nvme0n1/queue/ und setze die Wahl pro Gerät. Wichtig: Ich dokumentiere Änderungen, damit Wartung und Rollback ohne Rätselraten gelingen.
Persistenz und Automatisierung im Betrieb
Im Alltag setze ich Wiederholbarkeit über Automatisierung durch. Drei Wege haben sich bewährt:
- udev-Regeln: Beispiel für alle HDDs (rotational=1)
echo 'ACTION=="add|change", KERNEL=="sd*", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="mq-deadline"' > /etc/udev/rules.d/60-io-scheduler.rules, dannudevadm control --reload-rules && udevadm trigger. - systemd-tmpfiles: Für spezifische Geräte definiere ich
/etc/tmpfiles.d/blk.confmit Zeilen wiew /sys/block/sdX/queue/scheduler - - - - mq-deadline, die beim Boot schreiben. - Konfig-Management: In Ansible/Salt lege ich Geräteklassen (NVMe, HDD) an und verteile konsistente Defaults samt Dokumentation und Rollback.
Hinweis: elevator= als Kernel-Parameter galt für den alten Single-Queue-Pfad. In blk-mq bestimme ich die Wahl pro Gerät. Bei Stacks (dm-crypt, LVM, MD) setze ich die Vorgabe am Top-Device, dazu mehr weiter unten.
Workloads im Hosting: Muster erkennen und richtig handeln
Ich analysiere zunächst die Last: Viele kleine Reads deuten auf Webfrontends hin, Sync-heavy Writes auf Datenbanken und Log-Pipelines, große sequenzielle Streams auf Backups oder Archiv. Werkzeuge wie iostat, vmstat und blktrace zeigen Warteschlangen, Latenzen und Merge-Effekte. Bei auffälliger CPU-Leerlaufzeit durch I/O verweise ich auf I/O-Wait verstehen, um Engpässe strukturiert zu beheben. Danach teste ich 1–2 Scheduler-Kandidaten in identischen Zeitfenstern. Erst Messergebnisse entscheiden, nicht Bauchgefühl oder Mythen.
Messpraxis vertiefen: reproduzierbare Benchmarks
Für belastbare Entscheidungen nutze ich kontrollierte fio-Profile und bestätige mit echten Applikations-Tests:
- Zufalls-Reads (Web/Cache):
fio --name=rr --rw=randread --bs=4k --iodepth=32 --numjobs=4 --runtime=120 --time_based --filename=/mnt/testfile --direct=1 - Zufalls-Mix (DB):
fio --name=randmix --rw=randrw --rwmixread=70 --bs=8k --iodepth=64 --numjobs=8 --runtime=180 --time_based --direct=1 - Sequenziell (Backup):
fio --name=seqw --rw=write --bs=1m --iodepth=128 --numjobs=2 --runtime=120 --time_based --direct=1
Parallel logge ich iostat -x 1, pidstat -d 1 und notiere P95/P99-Latenzen aus fio. Für Tiefendiagnosen setze ich blktrace oder eBPF-Tools wie biolatency ein. Wichtig: Ich messe zu gleichen Tageszeiten, gleiche Lastfenster, gleiche Dateigrößen. Cache-Effekte minimiere ich mit direct=1 und sauberen Pre-Conditions (z. B. Pre-Fill auf dem Volume).
Dateisysteme und I/O Scheduler: Zusammenspiel zählt
Das Dateisystem beeinflusst die I/O-Charakteristik, daher prüfe ich dessen Journal-Modus, Queue-Tiefe und Sync-Verhalten sehr genau. EXT4 und XFS arbeiten effizient mit mq-deadline, während ZFS vieles selbst puffert und aggregiert. Auf Hosts mit ZFS beobachte ich den Scheduler-Effekt häufig geringer, weil ZFS die Ausgabe bereits formt. Für Vergleiche nutze ich identische Mount-Optionen und Workloads. Wer Optionen abwägt, findet in EXT4, XFS oder ZFS hilfreiche Perspektiven auf Storage-Tuning.
Writeback, Cache und Barrieren: die oft übersehene Hälfte
Scheduler können nur so gut wirken, wie es das Writeback-Subsystem erlaubt. Ich prüfe daher stets:
- dirty-Parameter:
sysctl vm.dirty_background_bytes,vm.dirty_bytes,vm.dirty_expire_centisecssteuern, wann und wie aggressiv der Kernel schreibt. Für Datenbanken senke ich oft Burst-Spitzen, um P99 stabil zu halten. - Barrieren/Flush: Optionen wie EXT4
barrierbzw. XFS Default-Flushes sichere ich nur ab, wenn Hardware (z. B. BBWC) sie übernimmt. „nobarrier“ ohne Stromschutz ist riskant. - Device Write-Cache: Ich verifiziere Write-Cache-Einstellungen des Controllers, damit
fsyncwirklich auf dem Medium landet und nicht nur im Cache.
Wer Writeback glättet, entlastet den Scheduler – Deadlines bleiben verlässlich, und BFQ muss weniger gegen plötzliche Flush-Wellen anarbeiten.
Virtualisierung, Container und Cloud: Wer plant wirklich?
In VMs steuert der Hypervisor den physischen I/O-Fluss, weshalb ich im Gast oft Noop/none wähle, um doppelte Logik zu vermeiden. Auf dem Host selbst nutze ich mq-deadline oder BFQ je nach Gerät und Aufgabe. Bei Cloud-Volumes (z. B. Netzwerk-Block-Storage) liegen Teile der Planung im Backend; daher messe ich reale Latenzen statt auf Annahmen zu vertrauen. Für Container-Hosts mit stark gemischter Last bringt BFQ häufig bessere Interaktivität. In homogenen Batch-Clustern mit Flash-Only setzt sich Noop durch, weil jede CPU-Zeit zählt und Controller effizient arbeiten.
RAID, LVM, MD und Multipath: wo der Scheduler greift
In gestapelten Block-Stacks setze ich den Scheduler am Top-Device an, denn dort liegen die relevanten Queues:
- LVM/dm-crypt: Scheduler am
/dev/dm-*bzw./dev/mapper/<lv>setzen. Die physischen PVs lasse ich meist aufnone, damit Merging/Sortierung nicht doppelt passiert. - MD-RAID: Am
/dev/mdXentscheiden; darunterliegendesdXGeräte bleiben unaufgeregt aufnone. Hardware-RAID wird wie ein einzelnes Blockdevice behandelt. - Multipath: Am Multipath-Mapper (
/dev/mapper/mpatha) festlegen; Pfad-Devices darunter aufnone.
Wichtig: Ich trenne Tests nach Pool und Redundanz-Level (RAID1/10 vs. RAID5/6). Paritäts-RAIDs reagieren empfindlicher auf Random-Writes; hier gewinnt mq-deadline oft durch konsequente Read-Deadlines und geordnete Ausgabe.
Tuning-Strategien: Schritt für Schritt zur verlässlichen Leistung
Ich starte mit einer Basis-Messung: aktuelle Antwortzeiten, Durchsatz, 95./99.-Perzentile und CPU-Load. Danach ändere ich nur einen Faktor, typischerweise den Scheduler, und wiederhole dieselbe Last. Tools wie fio helfen kontrolliert, doch ich bestätige jede Hypothese mit realen Applikations-Tests. Für Datenbanken eignen sich eigene Benchmarks, die Transaktionen und fsync-Verhalten abbilden. Erst wenn die Messung stabil ist, schreibe ich die Wahl fest und dokumentiere das Warum.
Queue-Tiefe, Readahead und CPU-Affinität
Neben dem Scheduler beeinflussen Queue-Parameter die Praxis stark:
- Queue-Tiefe:
/sys/block/<dev>/queue/nr_requestslimitiert Pending-Requests pro Hardware-Queue. NVMe verträgt hohe Tiefe (hoher Durchsatz), HDDs profitieren von moderater Tiefe (stabilere Latenz). - Readahead:
/sys/block/<dev>/queue/read_ahead_kbbzw.blockdev --getra/setra. Für sequenzielle Workloads etwas höher, für Random niedrig halten. - rq_affinity: Mit
/sys/block/<dev>/queue/rq_affinityauf 2 sorge ich dafür, dass I/O-Completion bevorzugt auf dem erzeugenden CPU-Core landet – das reduziert Cross-CPU-Kosten. - rotational: Ich verifiziere, dass SSDs
rotational=0melden, damit der Kernel keine HDD-Heuristiken anwendet. - Merges:
/sys/block/<dev>/queue/nomergeskann Merges reduzieren (2=aus). Für NVMe-Mikrolatenz teils sinnvoll, für HDDs meist nachteilig. - io_poll (NVMe): Polling kann Latenzen senken, braucht aber CPU. Ich aktiviere es gezielt bei Low-Latency-Anforderungen.
Scheduler-Tunables im Detail
Je nach Scheduler stehen sinnvolle Feinschrauben bereit:
- mq-deadline:
/sys/block/<dev>/queue/iosched/read_expire(ms, typisch klein),write_expire(größer),fifo_batch(Batch-Größe),front_merges(0/1). Ich halteread_expirekurz, um P95-Reads zu schützen, und justierefifo_batchje nach Gerät. - BFQ:
slice_idle(Idle-Zeit zur Sequenznutzung),low_latency(0/1) für reaktionsfreudige Interaktivität. Mitbfq.weightin Cgroups steuere ich relative Anteile sehr fein. - none/noop: Kaum Stellschrauben, aber die Umgebung (Queue-Tiefe, Readahead) entscheidet über die Resultate.
Ich ändere immer nur einen Parameter und halte die Änderung strikt fest – so bleibt klar, was welchen Effekt hatte.
Häufige Fallstricke und wie ich sie vermeide
Gemischte Pools aus HDD und SSD hinter einem RAID-Controller verfälschen Tests, daher trenne ich Messungen pro Gruppe. Ich vergesse nicht, dass der Scheduler pro Blockgerät gilt – LVM-Mapper und MD-Devices betrachte ich getrennt. Persistenz rutscht gern durch: Ohne udev-Regel oder Kernel-Parameter steht nach Reboot wieder der Default. Cgroups und I/O-Prioritäten bleiben oft ungenutzt, obwohl sie Fairness deutlich schärfen. Und ich prüfe stets Queue-Tiefe, Writeback und Filesystem-Optionen, damit die gewählte Logik ihr Potenzial zeigt.
Troubleshooting: Symptome gezielt lesen
Wenn die Messwerte kippen, deute ich Muster und leite konkrete Schritte ab:
- Hohe P99-Latenz bei vielen Reads: Prüfen, ob Writes Reads verdrängen. Mit mq-deadline testen,
read_expiresenken, Writeback glätten (vm.dirty_*anpassen). - 100% util auf HDD, niedriger Durchsatz: Seeks dominieren. BFQ oder mq-deadline probieren, Readahead reduzieren, Queue-Tiefe mäßigen.
- Gute Durchsatzwerte, aber UI ruckelt: Interaktivität leidet. BFQ aktivieren, kritische Dienste per
ionice -c1oder Cgroup-Gewichte bevorzugen. - Starke Varianz je nach Tageszeit: Geteilte Ressourcen. Mit Cgroups isolieren, Scheduler je Pool wählen, Backups auf Off-Peak verschieben.
- NVMe-Timeouts im dmesg: Backend oder Firmware-Thema.
io_polltestweise deaktivieren, Firmware/Driver prüfen, Pfad-Redundanz (Multipath) verifizieren.
Kurz zusammengefasst: Klare Entscheidungen für Hosting-Alltag
Für Flash-Storage und Gäste entscheide ich mich häufig für Noop, um Overhead zu sparen und Controller arbeiten zu lassen. In Allround-Servern mit HDD oder RAID liefert mq-deadline verlässliche Latenz und hohe Nutzbarkeit. Bei vielen aktiven Nutzern und interaktiver Last sorgt BFQ für faire Anteile und spürbare Reaktionsfreude. Ich messe vor jeder Festschreibung mit realen Workloads und beobachte die Effekte auf P95/P99. So treffe ich nachvollziehbare Entscheidungen, halte Systeme flott und stabilisiere die Server-Performance im Tagesgeschäft.

