{"id":18873,"date":"2026-04-09T15:07:49","date_gmt":"2026-04-09T13:07:49","guid":{"rendered":"https:\/\/webhosting.de\/kernel-io-scheduler-tuning-hosting-performance-raidboost\/"},"modified":"2026-04-09T15:07:49","modified_gmt":"2026-04-09T13:07:49","slug":"kernel-io-scheduler-tuning-hosting-performance-raidboost","status":"publish","type":"post","link":"https:\/\/webhosting.de\/en\/kernel-io-scheduler-tuning-hosting-performance-raidboost\/","title":{"rendered":"Kernel I\/O Scheduler Tuning: Optimization for hosting performance"},"content":{"rendered":"<p>Mit I\/O Scheduler Tuning optimiere ich gezielt den <strong>Kernel<\/strong>-Pfad f\u00fcr Speicherzugriffe und senke Latenz in Hosting-Umgebungen. Der Beitrag zeigt praxisnah, wie ich Linux-Disk-Scheduling an Hardware und Workload anpasse, um <strong>hosting<\/strong> performance sicher zu steigern.<\/p>\n\n<h2>Zentrale Punkte<\/h2>\n\n<p>Die folgenden Stichpunkte geben dir einen schnellen \u00dcberblick \u00fcber die Inhalte dieses Beitrags.<\/p>\n<ul>\n  <li><strong>Scheduler-Wahl<\/strong>: Noop\/none, mq-deadline, BFQ, Kyber je nach Hardware und Workload<\/li>\n  <li><strong>Messstrategie<\/strong>: Fio, iostat, P95\/P99, IOPS und Durchsatz vor\/nach \u00c4nderungen<\/li>\n  <li><strong>Feineinstellungen<\/strong>: Readahead, RQ-Affinity, Cgroups, ionice f\u00fcr QoS<\/li>\n  <li><strong>Persistenz<\/strong>: udev-Regeln und GRUB-Parameter f\u00fcr dauerhafte Profile<\/li>\n  <li><strong>Praxis<\/strong>: Troubleshooting bei Latenzspitzen, Fairness und NVMe-Spezifika<\/li>\n<\/ul>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img fetchpriority=\"high\" decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/04\/serverraum-optimierung-3958.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Wie Linux-Disk-Scheduling arbeitet<\/h2>\n\n<p>Ich sehe den I\/O-Scheduler als Schaltzentrale, die Anfragen in <strong>Queues<\/strong> sortiert, zusammenf\u00fchrt und priorisiert. Bei HDDs vermeide ich teure Kopfbewegungen, indem ich Anfragen nach Blockadressen ordne und so Suchzeiten reduziere. Auf SSDs und NVMe dominiert Parallelit\u00e4t, weshalb das Multi-Queue-Subsystem blk-mq den Pfad breiter macht und auf mehrere <strong>CPUs<\/strong> verteilt. Das senkt Latenzen, gl\u00e4ttet Peaks und h\u00e4lt den Durchsatz auf Kurs, selbst wenn viele Dienste gleichzeitig schreiben und lesen. Im Hosting treffen Webserver, Datenbanken und Backup-Jobs aufeinander, daher richte ich Scheduling immer an den dominanten Zugriffsmustern aus.<\/p>\n\n<h2>Die g\u00e4ngigen Scheduler kurz erkl\u00e4rt<\/h2>\n\n<p>F\u00fcr NVMe und moderne SSDs w\u00e4hle ich h\u00e4ufig <strong>none<\/strong> (\u00e4quivalent zu Noop im blk-mq-Kontext), weil der Controller intern optimiert und jeder zus\u00e4tzliche Overhead kostet. mq-deadline setzt feste Fristen f\u00fcr Reads und Writes, priorisiert Lesevorg\u00e4nge und liefert in gemischten Server-Lasten konstante Antwortzeiten. BFQ verteilt Bandbreite fair \u00fcber Prozesse und eignet sich in Multi-Tenant-Setups, in denen einzelne VMs sonst die Platte belegen w\u00fcrden. Kyber zielt auf geringe Latenzen und bremst ankommende Requests, wenn Zielzeiten rei\u00dfen. CFQ gilt als Altlast und passt kaum zu NVMe; ich greife nur zu CFQ, wenn Legacy-Setups es erfordern oder Tests klare Vorteile zeigen; einen ausf\u00fchrlichen \u00dcberblick gebe ich hier: <a href=\"https:\/\/webhosting.de\/io-scheduler-linux-noop-mq-deadline-bfq-serverboost\/\">I\/O-Scheduler Guide<\/a>.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/04\/kernel_scheduler_tuning_4532.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>I\/O Scheduler Tuning Schritt f\u00fcr Schritt<\/h2>\n\n<p>Ich starte mit einem klaren <strong>Baseline<\/strong>-Messlauf, damit ich Gewinne objektiv zeigen kann. Dazu nutze ich fio f\u00fcr synthetische Muster, iostat f\u00fcr Ger\u00e4testatistiken und sammle P95\/P99-Latenzen f\u00fcr Reads und Writes. Danach pr\u00fcfe ich den aktiven Scheduler pro Ger\u00e4t und \u00e4ndere ihn zur Laufzeit, um schnell gegenzutesten. Persistente Anpassungen setze ich erst, wenn Messungen stabil zeigen, dass die Wahl passt. So vermeide ich Fehlentscheidungen, die sp\u00e4ter teure Rollbacks erzwingen.<\/p>\n\n<pre><code># Aktuellen Scheduler pr\u00fcfen\ncat \/sys\/block\/&lt;dev&gt;\/queue\/scheduler\n\n# Laufend wechseln (Beispiel: nvme0n1 auf mq-deadline)\necho mq-deadline | sudo tee \/sys\/block\/nvme0n1\/queue\/scheduler\n\n# Schneller Vergleich mit fio (Random Reads 4k)\nfio --name=rr --rw=randread --bs=4k --iodepth=32 --numjobs=4 --runtime=60 --filename=\/dev\/nvme0n1\n<\/code><\/pre>\n\n<p>Ich behalte die CPU-Last im Blick, weil ein ungeeigneter <strong>Scheduler<\/strong> zus\u00e4tzliche Context-Switches erzeugt und damit die Nettoleistung dr\u00fcckt. Sobald Latenzen fallen und der Durchsatz steigt, sichere ich die Entscheidung und dokumentiere Testprofile. Jeder Schritt folgt einer \u00c4nderung, dann einer Messung, damit ich Ursache und Wirkung sauber trennen kann. Diese Disziplin zahlt sich aus, wenn mehrere Plattenklassen im Server verbaut sind und einzelne Ger\u00e4te anders reagieren.<\/p>\n\n<h2>Feineinstellungen: Readahead, RQ-Affinity, Cgroups<\/h2>\n\n<p>Nach der Scheduler-Wahl justiere ich die <strong>Queue<\/strong>-Parameter f\u00fcr die Last. F\u00fcr sequenzielle Backups hebe ich Readahead an, bei Random-IO senke ich ihn, damit ich keine unn\u00f6tigen Seiten lade. Mit RQ-Affinity sorge ich daf\u00fcr, dass Completions auf dem Kern landen, der die Anfrage erzeugt hat, was Latenz und Cache-Locality verbessert. Prozesse wie Backups und Indizierung stufe ich mit ionice ab, damit Webanfragen nicht leiden. In Multi-Tenant-Umgebungen regle ich Bandbreite und IOPS \u00fcber Cgroups v2, um harte Grenzen pro Kunde zu setzen.<\/p>\n\n<pre><code># Readahead f\u00fcr sequenzielle Muster\necho 128 | sudo tee \/sys\/block\/&lt;dev&gt;\/queue\/read_ahead_kb\n\n# RQ-Affinity: 2 = Completion auf erzeugendem Kern\necho 2 | sudo tee \/sys\/block\/&lt;dev&gt;\/queue\/rq_affinity\n\n# Backup-Prozess absenken\nionice -c2 -n7 -p &lt;pid&gt;\n\n# Cgroup v2: Gewicht und Limit (Beispielmajor:minor 8:0)\necho 1000 | sudo tee \/sys\/fs\/cgroup\/hosting\/io.weight\necho \"8:0 rbps=50M wbps=25M\" | sudo tee \/sys\/fs\/cgroup\/hosting\/io.max\n<\/code><\/pre>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/04\/kernel-io-tuning-hosting-9034.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Welche Wahl passt f\u00fcr Hosting-Profile?<\/h2>\n\n<p>Ich entscheide die <strong>Scheduler<\/strong>-Wahl nach Hardwareklasse, Zugriffsmuster und Zielgr\u00f6\u00dfe (Latenz vs. Durchsatz vs. Fairness). NVMe-SSDs in Single-Tenant-VMs profitieren meist von none, weil der Controller umfangreiche Optimierung \u00fcbernimmt und jede Software-Schicht z\u00e4hlt. F\u00fcr gemischte Lese-\/Schreiblast auf SSDs setze ich oft auf mq-deadline, da es Leseanfragen priorisiert und so Antwortzeiten sch\u00fctzt. In Shared-Hosting-Umgebungen w\u00e4hle ich BFQ, um Fairness zwischen Kunden sicherzustellen und Bandbreitenmonopole zu verhindern. Kyber ziehe ich heran, wenn Ziel-Latenzen kritisch sind und ich f\u00fcr bestimmte Workloads harte Grenzen einhalten muss.<\/p>\n\n<table>\n  <thead>\n    <tr>\n      <th>Scheduler<\/th>\n      <th>Geeignete Hardware<\/th>\n      <th>Typische Workloads<\/th>\n      <th>Vorteile<\/th>\n      <th>Hinweise<\/th>\n    <\/tr>\n  <\/thead>\n  <tbody>\n    <tr>\n      <td>Noop\/none<\/td>\n      <td><strong>NVMe<\/strong>, moderne SSD<\/td>\n      <td>Viele parallele Reads\/Writes, VMs<\/td>\n      <td>Minimaler Overhead, hohe Parallelit\u00e4t<\/td>\n      <td>Controller \u00fcbernimmt Sortierung; testen in SAN\/RAID<\/td>\n    <\/tr>\n    <tr>\n      <td>mq-deadline<\/td>\n      <td>SSD, <strong>SAS<\/strong>, schnelle HDD<\/td>\n      <td>Gemischte Web-\/DB-Lasten<\/td>\n      <td>Leselatenzen priorisiert, guter Durchsatz<\/td>\n      <td>Deadline-Werte konservativ; Feintuning m\u00f6glich<\/td>\n    <\/tr>\n    <tr>\n      <td>BFQ<\/td>\n      <td>SSD\/HDD in Multi-Tenant<\/td>\n      <td>Viele Nutzer, cgroups<\/td>\n      <td>Klare Fairness und Bandbreitenkontrolle<\/td>\n      <td>Etwas Verwaltungsaufwand, sauber gewichten<\/td>\n    <\/tr>\n    <tr>\n      <td>Kyber<\/td>\n      <td>SSD, <strong>NVMe<\/strong><\/td>\n      <td>Latenz-kritische Dienste<\/td>\n      <td>Ziel-Latenzen steuerbar<\/td>\n      <td>Genau messen, um Throttling richtig zu setzen<\/td>\n    <\/tr>\n    <tr>\n      <td>CFQ<\/td>\n      <td>Legacy-Hardware<\/td>\n      <td>Alt-Workloads<\/td>\n      <td>Fr\u00fchere Standardl\u00f6sung<\/td>\n      <td>Selten sinnvoll auf moderner NVMe\/SSD<\/td>\n    <\/tr>\n  <\/tbody>\n<\/table>\n\n<h2>Praxisnahe Profile und Messwerte<\/h2>\n\n<p>F\u00fcr Webserver mit vielen kleinen <strong>Reads<\/strong> z\u00e4hlt die P95-Latenz mehr als reine IOPS, daher pr\u00fcfe ich Get-Requests mit Keep-Alive und TLS im Zusammenspiel. Datenbanken bringen Sync-Writes ins Spiel, weshalb ich hier Flush-Verhalten und Fsync-Kosten mit fio-Jobfiles simuliere. Backup-Fenster haben oft sequentielle Str\u00f6me; hier messe ich Durchsatz in MB\/s und achte darauf, dass Frontend-Anfragen nicht zu lange warten. In meinen Tests sehe ich je nach Ausgangslage 20\u201350 % k\u00fcrzere Antwortzeiten, wenn Scheduler und Readahead zu den Workloads passen. Wer mehr Kontext zur Messung des Plattendurchsatzes braucht, findet hier einen Einstieg: <a href=\"https:\/\/webhosting.de\/server-disk-throughput-hosting-leistung-perfopt\/\">Disk-Durchsatz im Hosting<\/a>.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/04\/kernel_io_scheduler_hosting_3287.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Persistente Konfiguration und Automatisierung<\/h2>\n\n<p>Ich verankere die <strong>Wahl<\/strong> dauerhaft per udev-Regel, damit Ger\u00e4te nach Reboots direkt im passenden Modus starten. F\u00fcr NVMe setze ich oft none, f\u00fcr SSDs mq-deadline und f\u00fcr rotierende Medien BFQ, wenn Fairness im Vordergrund steht. \u00dcber GRUB lege ich optional einen globalen Default, falls ich ein homogenes Setup betreibe. Die Regeln halte ich knapp und dokumentiere sie im Konfigurations-Repository, damit das Team sie nachverfolgen kann. F\u00fcr tiefergehende Kernel-Optimierung erg\u00e4nzt dieser Beitrag das Setup: <a href=\"https:\/\/webhosting.de\/linux-kernel-performance-hosting-optimierung-kernelboost\/\">Kernel-Performance im Hosting<\/a>.<\/p>\n\n<pre><code># \/etc\/udev\/rules.d\/60-ioschedulers.rules\n\n# NVMe: none\nACTION==\"add|change\", KERNEL==\"nvme[0-9]*\", ATTR{queue\/scheduler}=\"none\"\n\n# SSDs: mq-deadline\nACTION==\"add|change\", KERNEL==\"sd[a-z]|vd[a-z]\", ATTR{rotational}==\"0\", ATTR{queue\/scheduler}=\"mq-deadline\"\n\n# HDDs: BFQ\nACTION==\"add|change\", KERNEL==\"sd[a-z]\", ATTR{rotational}==\"1\", ATTR{queue\/scheduler}=\"bfq\"\n\n# Regeln neu laden\/testen\nudevadm control --reload\nudevadm trigger\n<\/code><\/pre>\n\n<pre><code># Optionaler globaler Default \u00fcber GRUB\n# \/etc\/default\/grub\nGRUB_CMDLINE_LINUX_DEFAULT=\"elevator=mq-deadline\"\nupdate-grub\n<\/code><\/pre>\n\n<h2>QoS mit Cgroups v2 und ionice<\/h2>\n\n<p>Damit kein Job die Platte <strong>blockiert<\/strong>, setze ich auf QoS-Regeln mit Cgroups v2 und erg\u00e4nze Priorit\u00e4ten \u00fcber ionice. F\u00fcr Premium-Tenants hebe ich io.weight an, w\u00e4hrend ich f\u00fcr laute Nachbarn harte Grenzen mit io.max setze. Systemd-Units binde ich direkt an Cgroups, sodass Dienste beim Start automatisch in die richtige Klasse rutschen. Kurzfristige Wartungsarbeiten drossele ich tempor\u00e4r, damit Kundenanfragen weiter fl\u00fcssig laufen. Dieses Zusammenspiel aus Gewichtung, Limits und Prozess-Priorit\u00e4t schafft berechenbare Antwortzeiten auch unter Last.<\/p>\n\n<pre><code># Cgroup anlegen und Limits setzen\nmkdir -p \/sys\/fs\/cgroup\/hosting\necho 1000 | tee \/sys\/fs\/cgroup\/hosting\/io.weight\necho \"8:0 rbps=100M wbps=60M\" | tee \/sys\/fs\/cgroup\/hosting\/io.max\n\n# Prozess in Cgroup verschieben\necho &lt;pid&gt; | tee \/sys\/fs\/cgroup\/hosting\/cgroup.procs\n\n# Niedrige IO-Priorit\u00e4t f\u00fcr Nebenjobs\nionice -c2 -n7 -p &lt;pid&gt;\n<\/code><\/pre>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/04\/kernel_optimierung_desk_4856.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Monitoring und Troubleshooting<\/h2>\n\n<p>Ich halte Telemetrie stets <strong>nah<\/strong> an den Workloads, sonst verfehle ich Entscheidungen. Mit iostat lese ich Service-Zeiten und Queue-Tiefen, mit blktrace analysiere ich Request-Fl\u00fcsse, und mit sar\/dstat sehe ich Systemlast im Zeitverlauf. F\u00fcr Latenzen schaue ich nicht nur auf Mittelwerte, sondern immer auf P95\/P99, weil sp\u00fcrbare Haker dort sichtbar werden. Wenn P95 gut ist, P99 aber kippt, justiere ich Queue-Tiefe oder RQ-Affinity und pr\u00fcfe konkurrierende Jobs. Nach jeder Korrektur vergleiche ich dieselben Kennzahlen, damit die Wirkung belastbar bleibt.<\/p>\n\n<h2>Typische Stolpersteine und Abhilfe<\/h2>\n\n<p>Hohe <strong>Latenz<\/strong> auf SSDs deutet oft auf einen unpassenden Scheduler hin; ich teste dann sofort mq-deadline und pr\u00fcfe, ob Reads schneller werden. Unfaire Verteilung in Multi-Tenant-Setups l\u00f6se ich mit BFQ und klaren Cgroup-Gewichten, damit starke Kunden schw\u00e4chere nicht verdr\u00e4ngen. NVMe-Timeouts deuten auf Firmware oder zu aggressives Polling; in solchen F\u00e4llen deaktiviere ich io_poll und senke die Tiefe, bis Stabilit\u00e4t zur\u00fcckkehrt. Schwankender Durchsatz in Backup-Fenstern l\u00e4sst sich h\u00e4ufig mit angepasstem Readahead gl\u00e4tten, besonders wenn gro\u00dfe Dateien dominieren. Drehen mehr Faktoren gleichzeitig, gehe ich schrittweise vor: eine \u00c4nderung, dann Messen, dann die n\u00e4chste.<\/p>\n\n<h2>Scheduler-Tunables im Detail<\/h2>\n\n<p>Nachdem die Grundwahl sitzt, drehe ich an den Stellschrauben der jeweiligen Scheduler. Ich beginne immer damit, mir die verf\u00fcgbaren Parameter je Ger\u00e4t anzusehen, da sie je nach Kernel und Distro variieren.<\/p>\n\n<pre><code># Verf\u00fcgbare Tunables anzeigen\nls -1 \/sys\/block\/&lt;dev&gt;\/queue\/iosched\ncat \/sys\/block\/&lt;dev&gt;\/queue\/iosched\/*\n\n# Beispiel: mq-deadline konservativer f\u00fcr Write-lastige Jobs\necho 100 | sudo tee \/sys\/block\/&lt;dev&gt;\/queue\/iosched\/read_expire\necho 500 | sudo tee \/sys\/block\/&lt;dev&gt;\/queue\/iosched\/write_expire\necho 1   | sudo tee \/sys\/block\/&lt;dev&gt;\/queue\/iosched\/front_merges\n\n# Beispiel: BFQ f\u00fcr striktere Fairness und geringere Idle-Zeiten\necho 1   | sudo tee \/sys\/block\/&lt;dev&gt;\/queue\/iosched\/low_latency\necho 0   | sudo tee \/sys\/block\/&lt;dev&gt;\/queue\/iosched\/slice_idle\n<\/code><\/pre>\n\n<p>Bei mq-deadline reguliere ich vor allem <em>read_expire<\/em>\/<em>write_expire<\/em> (in Millisekunden) und <em>front_merges<\/em> f\u00fcr das Zusammenf\u00fchren anliegender Requests. Bei BFQ schalte ich je nach Tenant-Dichte <em>low_latency<\/em> und <em>slice_idle<\/em>, um Wartezeiten zwischen Flows zu reduzieren. Ich dokumentiere jede \u00c4nderung mit Messwerten, denn falsche Expires k\u00f6nnen bei Burst-Last ungewollte Latenzspitzen ausl\u00f6sen.<\/p>\n\n<h2>Dateisystem- und Mount-Optionen<\/h2>\n\n<p>Scheduler-Tuning entfaltet sich erst richtig, wenn das Dateisystem dazu passt. Ich achte auf:<\/p>\n<ul>\n  <li><strong>relatime\/noatime<\/strong>: unn\u00f6tige Metadaten-Schreibzugriffe vermeiden.<\/li>\n  <li><strong>discard vs. fstrim<\/strong>: Auf SSDs\/NVMe nutze ich meist periodisches fstrim statt Online-Discard, um Latenzspitzen zu vermeiden.<\/li>\n  <li><strong>Journaling<\/strong>: F\u00fcr ext4 bew\u00e4hren sich <em>data=ordered<\/em> (Default) und ein passendes <em>commit=<\/em>-Intervall (z. B. 10\u201330s je nach Datenverlust-Toleranz).<\/li>\n  <li><strong>Barriers<\/strong>: Schreibe-Barrieren bleiben aktiv; ich deaktiviere sie nicht, au\u00dfer die Hardware garantiert Stromausfallschutz (Batterie\/Capacitor).<\/li>\n<\/ul>\n\n<pre><code># Beispiel \/etc\/fstab f\u00fcr ext4\nUUID=&lt;uuid&gt; \/data ext4 defaults,noatime,commit=20 0 2\n\n# Periodisches TRIM statt discard-Option aktivieren\nsystemctl enable fstrim.timer\nsystemctl start fstrim.timer\n<\/code><\/pre>\n\n<p>F\u00fcr XFS setze ich ebenfalls <em>noatime<\/em> und bevorzuge fstrim.timer. Journal- oder Barrier-Optionen sind distributionsabh\u00e4ngig; ich teste immer die konkrete Kernel\/FS-Kombination und messe P95\/P99.<\/p>\n\n<h2>RAID, LVM, DM-crypt und Multipath<\/h2>\n\n<p>In gestapelten Setups (Device Mapper, LVM, mdraid, Multipath) lege ich den Scheduler dort fest, wo die Applikation I\/O sieht \u2013 also am <strong>Top-Level-Device<\/strong> \u2013 und verhindere doppelte Sortierung darunter.<\/p>\n\n<pre><code># Scheduler auf dem Top-Level (z. B. dm-0) setzen\necho mq-deadline | sudo tee \/sys\/block\/dm-0\/queue\/scheduler\n\n# Unterliegenden NVMe\/SAS-Devices \"none\", um Doppel-Scheduling zu vermeiden\nfor d in \/sys\/block\/nvme*n1 \/sys\/block\/sd*; do echo none | sudo tee $d\/queue\/scheduler; done\n\n# mdraid: Readahead und Stripe-Cache (RAID5\/6) optimieren\nsudo blockdev --setra 4096 \/dev\/md0\necho 4096 | sudo tee \/sys\/block\/md0\/md\/stripe_cache_size\n<\/code><\/pre>\n\n<p>Bei verschl\u00fcsselten Volumes (dm-crypt\/LUKS) achte ich auf CPU-Offload (AES-NI) und darauf, dass der I\/O-Pfad nicht unn\u00f6tig \u00fcber Workqueues wandert. Ich messe gezielt Sync-Write-Latenzen, da diese durch die Crypto-Schicht st\u00e4rker ansteigen k\u00f6nnen. In Multipath-Umgebungen (SAN\/iSCSI) setze ich den Scheduler auf dem Multipath-Ger\u00e4t (dm-X) und \u00fcberpr\u00fcfe, ob Pfad-Failover keine Ausrei\u00dfer erzeugt.<\/p>\n\n<h2>Virtualisierung und Container: Host vs. Gast<\/h2>\n\n<p>Im KVM-Stack trenne ich bewusst zwischen Host und Gast. Im <strong>Gast<\/strong> setze ich f\u00fcr virtio-Devices meist <em>none<\/em>, damit der Hypervisor die Optimierung \u00fcbernimmt. Auf dem <strong>Host<\/strong> w\u00e4hle ich dann pro physischem Device den Scheduler passend zur Hardware (h\u00e4ufig none\/mq-deadline auf SSD\/NVMe).<\/p>\n\n<pre><code># Gast (virtio-blk\/virtio-scsi): Scheduler \"none\" setzen\necho none | sudo tee \/sys\/block\/vda\/queue\/scheduler\n\n# Host: QEMU mit iothreads und Multiqueue f\u00fcr virtio-blk\nqemu-system-x86_64 \\\n  -drive if=none,id=vd0,file=\/var\/lib\/libvirt\/images\/guest.qcow2,cache=none,aio=native \\\n  -object iothread,id=ioth0 \\\n  -device virtio-blk-pci,drive=vd0,num-queues=8,iothread=ioth0\n<\/code><\/pre>\n\n<p>Container binde ich direkt an Cgroups v2 und nutze systemd-Properties (IOWeight, IOReadBandwidthMax\/IOWriteBandwidthMax), damit Dienste automatisch mit den richtigen I\/O-Budgets starten. Wichtig: Nur auf einer Ebene priorisieren \u2013 entweder im Container oder im Host-Service \u2013 um sich widersprechende Regeln zu vermeiden.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/04\/hosting-serverraum-4857.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>NUMA, IRQ- und Polling-Optimierung<\/h2>\n\n<p>Auf Multi-Socket-Systemen halte ich I\/O und CPU <strong>NUMA-nah<\/strong>. Ich pr\u00fcfe die Verteilung der NVMe-Interrupts und passe sie bei Bedarf an, wenn irqbalance suboptimal arbeitet. Zus\u00e4tzlich nutze ich blk-mq-Optionen, um Completions lokal zu halten.<\/p>\n\n<pre><code># NVMe-Interrupts pr\u00fcfen und Kernmasken setzen (Beispiel)\ngrep -i nvme \/proc\/interrupts\necho &lt;hex-mask&gt; | sudo tee \/proc\/irq\/&lt;irq&gt;\/smp_affinity\n\n# blk-mq: Completions auf erzeugendem Kern\necho 2 | sudo tee \/sys\/block\/&lt;dev&gt;\/queue\/rq_affinity\n\n# Optional: I\/O-Polling je nach Workload testen (vorsichtig einsetzen)\necho 0 | sudo tee \/sys\/block\/&lt;dev&gt;\/queue\/io_poll\n<\/code><\/pre>\n\n<p>F\u00fcr NVMe kann ich \u00fcber Controller-Features die Interrupt-Coalescing-Parameter anpassen, um das Verh\u00e4ltnis aus CPU-Last und Latenz zu gl\u00e4tten. Ich taste mich hier in kleinen Schritten vor und pr\u00fcfe, ob P99 stabil bleibt oder ob Coalescing zu sichtbarer Tr\u00e4gheit f\u00fchrt.<\/p>\n\n<h2>Beispielhafte fio-Jobfiles und Messplan<\/h2>\n\n<p>Ich lege reproduzierbare Jobfiles an und notiere Kernel, Scheduler, Queue-Parameter und Filesystem-Mounts. So lassen sich Ergebnisse \u00fcber Wochen vergleichen.<\/p>\n\n<pre><code># db-sync.fio \u2013 DB-\u00e4hnliche Sync-Writes (ext4\/XFS)\n[global]\nioengine=libaio\ndirect=1\nfilename=\/dev\/&lt;dev&gt;\ntime_based=1\nruntime=90\nthread=1\nnumjobs=8\niodepth=1\n\n[randwrite-sync4k]\nrw=randwrite\nbs=4k\nfsync=1\n\n# web-randread.fio \u2013 Web-\u00e4hnliche Reads\n[global]\nioengine=libaio\ndirect=1\nfilename=\/dev\/&lt;dev&gt;\ntime_based=1\nruntime=90\nthread=1\nnumjobs=8\niodepth=32\n\n[randread-4k]\nrw=randread\nbs=4k\n<\/code><\/pre>\n\n<pre><code># Messrahmen\n# 1) Warmup 60s, 2) Messung 90s, 3) Cooldown 30s\n# Parallel: iostat, pidstat und blktrace laufen lassen\n\niostat -x 1 | tee iostat.log &\npidstat -dl 1 | tee pidstat.log &\nblktrace -d \/dev\/&lt;dev&gt; -o - | blkparse -i - -d trace.dump &\n\n# Nachlauf: P95\/P99 aus fio-JSON ziehen\nfio --output-format=json --output=fio.json db-sync.fio\njq '.jobs[].lat_ns[\"percentile\"]|{p95:.[\"95.000000\"],p99:.[\"99.000000\"]}' fio.json\n<\/code><\/pre>\n\n<p>Ich \u00e4ndere immer nur eine Variable, z. B. Scheduler oder read_ahead_kb, und vergleiche die identischen Jobfiles erneut. Erst wenn die Verbesserungen \u00fcber mehrere L\u00e4ufe konsistent sind, schreibe ich die Einstellungen fest.<\/p>\n\n<h2>Change-Management: sicher einf\u00fchren und zur\u00fcckrollen<\/h2>\n\n<p>In produktiven Hosting-Umgebungen rolle ich I\/O-\u00c4nderungen <strong>gestaffelt<\/strong> aus: ein Canary-Host, dann eine kleine AZ\/Cluster-Partie, erst danach der breite Rollout. Udev-Regeln versioniere ich und h\u00e4nge jede \u00c4nderung an ein Ticket mit Messwerten. F\u00fcr das Rollback halte ich ein Skript bereit, das die vorherigen Werte ausspielt (Scheduler, read_ahead_kb, Cgroup-Limits). So bleiben Eingriffe reversibel, wenn sich Workloads kurzfristig \u00e4ndern.<\/p>\n\n<h2>Zusammenfassung: So gehe ich vor<\/h2>\n\n<p>Ich starte mit einem klaren <strong>Istwert<\/strong>, messe Latenzen und Durchsatz und dokumentiere das Setup. Danach w\u00e4hle ich je Ger\u00e4t einen passenden Scheduler: none f\u00fcr NVMe\/virtuelle SSDs, mq-deadline f\u00fcr gemischte Server-Lasten, BFQ f\u00fcr geteilte Umgebungen mit vielen Nutzern. Anschlie\u00dfend feile ich an Readahead, RQ-Affinity und Prozess-Priorit\u00e4ten, damit Frontend-Workloads Vorrang behalten. Wenn Messungen konsistent zeigen, dass die Wahl tr\u00e4gt, fixiere ich sie \u00fcber udev\/GRUB und schreibe die Parameter fest. Monitoring bleibt aktiv, denn Workloads \u00e4ndern sich, und mit kleinen Korrekturen halte ich die <strong>Performance<\/strong> dauerhaft hoch.<\/p>","protected":false},"excerpt":{"rendered":"<p>Kernel I\/O Scheduler Tuning optimizes Linux disk scheduling for maximum hosting performance. Tips for Noop, mq-deadline &amp; BFQ.<\/p>","protected":false},"author":1,"featured_media":18866,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_crdt_document":"","inline_featured_image":false,"footnotes":""},"categories":[676],"tags":[],"class_list":["post-18873","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-server_vm"],"acf":[],"_wp_attached_file":null,"_wp_attachment_metadata":null,"litespeed-optimize-size":null,"litespeed-optimize-set":null,"_elementor_source_image_hash":null,"_wp_attachment_image_alt":null,"stockpack_author_name":null,"stockpack_author_url":null,"stockpack_provider":null,"stockpack_image_url":null,"stockpack_license":null,"stockpack_license_url":null,"stockpack_modification":null,"color":null,"original_id":null,"original_url":null,"original_link":null,"unsplash_location":null,"unsplash_sponsor":null,"unsplash_exif":null,"unsplash_attachment_metadata":null,"_elementor_is_screenshot":null,"surfer_file_name":null,"surfer_file_original_url":null,"envato_tk_source_kit":null,"envato_tk_source_index":null,"envato_tk_manifest":null,"envato_tk_folder_name":null,"envato_tk_builder":null,"envato_elements_download_event":null,"_menu_item_type":null,"_menu_item_menu_item_parent":null,"_menu_item_object_id":null,"_menu_item_object":null,"_menu_item_target":null,"_menu_item_classes":null,"_menu_item_xfn":null,"_menu_item_url":null,"_trp_menu_languages":null,"rank_math_primary_category":null,"rank_math_title":null,"inline_featured_image":null,"_yoast_wpseo_primary_category":null,"rank_math_schema_blogposting":null,"rank_math_schema_videoobject":null,"_oembed_049c719bc4a9f89deaead66a7da9fddc":null,"_oembed_time_049c719bc4a9f89deaead66a7da9fddc":null,"_yoast_wpseo_focuskw":null,"_yoast_wpseo_linkdex":null,"_oembed_27e3473bf8bec795fbeb3a9d38489348":null,"_oembed_c3b0f6959478faf92a1f343d8f96b19e":null,"_trp_translated_slug_en_us":null,"_wp_desired_post_slug":null,"_yoast_wpseo_title":null,"tldname":null,"tldpreis":null,"tldrubrik":null,"tldpolicylink":null,"tldsize":null,"tldregistrierungsdauer":null,"tldtransfer":null,"tldwhoisprivacy":null,"tldregistrarchange":null,"tldregistrantchange":null,"tldwhoisupdate":null,"tldnameserverupdate":null,"tlddeletesofort":null,"tlddeleteexpire":null,"tldumlaute":null,"tldrestore":null,"tldsubcategory":null,"tldbildname":null,"tldbildurl":null,"tldclean":null,"tldcategory":null,"tldpolicy":null,"tldbesonderheiten":null,"tld_bedeutung":null,"_oembed_d167040d816d8f94c072940c8009f5f8":null,"_oembed_b0a0fa59ef14f8870da2c63f2027d064":null,"_oembed_4792fa4dfb2a8f09ab950a73b7f313ba":null,"_oembed_33ceb1fe54a8ab775d9410abf699878d":null,"_oembed_fd7014d14d919b45ec004937c0db9335":null,"_oembed_21a029d076783ec3e8042698c351bd7e":null,"_oembed_be5ea8a0c7b18e658f08cc571a909452":null,"_oembed_a9ca7a298b19f9b48ec5914e010294d2":null,"_oembed_f8db6b27d08a2bb1f920e7647808899a":null,"_oembed_168ebde5096e77d8a89326519af9e022":null,"_oembed_cdb76f1b345b42743edfe25481b6f98f":null,"_oembed_87b0613611ae54e86e8864265404b0a1":null,"_oembed_27aa0e5cf3f1bb4bc416a4641a5ac273":null,"_oembed_time_27aa0e5cf3f1bb4bc416a4641a5ac273":null,"_tldname":null,"_tldclean":null,"_tldpreis":null,"_tldcategory":null,"_tldsubcategory":null,"_tldpolicy":null,"_tldpolicylink":null,"_tldsize":null,"_tldregistrierungsdauer":null,"_tldtransfer":null,"_tldwhoisprivacy":null,"_tldregistrarchange":null,"_tldregistrantchange":null,"_tldwhoisupdate":null,"_tldnameserverupdate":null,"_tlddeletesofort":null,"_tlddeleteexpire":null,"_tldumlaute":null,"_tldrestore":null,"_tldbildname":null,"_tldbildurl":null,"_tld_bedeutung":null,"_tldbesonderheiten":null,"_oembed_ad96e4112edb9f8ffa35731d4098bc6b":null,"_oembed_8357e2b8a2575c74ed5978f262a10126":null,"_oembed_3d5fea5103dd0d22ec5d6a33eff7f863":null,"_eael_widget_elements":null,"_oembed_0d8a206f09633e3d62b95a15a4dd0487":null,"_oembed_time_0d8a206f09633e3d62b95a15a4dd0487":null,"_aioseo_description":null,"_eb_attr":null,"_eb_data_table":null,"_oembed_819a879e7da16dd629cfd15a97334c8a":null,"_oembed_time_819a879e7da16dd629cfd15a97334c8a":null,"_acf_changed":null,"_wpcode_auto_insert":null,"_edit_last":null,"_edit_lock":null,"_oembed_e7b913c6c84084ed9702cb4feb012ddd":null,"_oembed_bfde9e10f59a17b85fc8917fa7edf782":null,"_oembed_time_bfde9e10f59a17b85fc8917fa7edf782":null,"_oembed_03514b67990db061d7c4672de26dc514":null,"_oembed_time_03514b67990db061d7c4672de26dc514":null,"rank_math_news_sitemap_robots":null,"rank_math_robots":null,"_eael_post_view_count":"361","_trp_automatically_translated_slug_ru_ru":null,"_trp_automatically_translated_slug_et":null,"_trp_automatically_translated_slug_lv":null,"_trp_automatically_translated_slug_fr_fr":null,"_trp_automatically_translated_slug_en_us":null,"_wp_old_slug":null,"_trp_automatically_translated_slug_da_dk":null,"_trp_automatically_translated_slug_pl_pl":null,"_trp_automatically_translated_slug_es_es":null,"_trp_automatically_translated_slug_hu_hu":null,"_trp_automatically_translated_slug_fi":null,"_trp_automatically_translated_slug_ja":null,"_trp_automatically_translated_slug_lt_lt":null,"_elementor_edit_mode":null,"_elementor_template_type":null,"_elementor_version":null,"_elementor_pro_version":null,"_wp_page_template":null,"_elementor_page_settings":null,"_elementor_data":null,"_elementor_css":null,"_elementor_conditions":null,"_happyaddons_elements_cache":null,"_oembed_75446120c39305f0da0ccd147f6de9cb":null,"_oembed_time_75446120c39305f0da0ccd147f6de9cb":null,"_oembed_3efb2c3e76a18143e7207993a2a6939a":null,"_oembed_time_3efb2c3e76a18143e7207993a2a6939a":null,"_oembed_59808117857ddf57e478a31d79f76e4d":null,"_oembed_time_59808117857ddf57e478a31d79f76e4d":null,"_oembed_965c5b49aa8d22ce37dfb3bde0268600":null,"_oembed_time_965c5b49aa8d22ce37dfb3bde0268600":null,"_oembed_81002f7ee3604f645db4ebcfd1912acf":null,"_oembed_time_81002f7ee3604f645db4ebcfd1912acf":null,"_elementor_screenshot":null,"_oembed_7ea3429961cf98fa85da9747683af827":null,"_oembed_time_7ea3429961cf98fa85da9747683af827":null,"_elementor_controls_usage":null,"_elementor_page_assets":[],"_elementor_screenshot_failed":null,"theplus_transient_widgets":null,"_eael_custom_js":null,"_wp_old_date":null,"_trp_automatically_translated_slug_it_it":null,"_trp_automatically_translated_slug_pt_pt":null,"_trp_automatically_translated_slug_zh_cn":null,"_trp_automatically_translated_slug_nl_nl":null,"_trp_automatically_translated_slug_pt_br":null,"_trp_automatically_translated_slug_sv_se":null,"rank_math_analytic_object_id":null,"rank_math_internal_links_processed":"1","_trp_automatically_translated_slug_ro_ro":null,"_trp_automatically_translated_slug_sk_sk":null,"_trp_automatically_translated_slug_bg_bg":null,"_trp_automatically_translated_slug_sl_si":null,"litespeed_vpi_list":null,"litespeed_vpi_list_mobile":null,"rank_math_seo_score":null,"rank_math_contentai_score":null,"ilj_limitincominglinks":null,"ilj_maxincominglinks":null,"ilj_limitoutgoinglinks":null,"ilj_maxoutgoinglinks":null,"ilj_limitlinksperparagraph":null,"ilj_linksperparagraph":null,"ilj_blacklistdefinition":null,"ilj_linkdefinition":null,"_eb_reusable_block_ids":null,"rank_math_focus_keyword":"I\/O Scheduler Tuning","rank_math_og_content_image":null,"_yoast_wpseo_metadesc":null,"_yoast_wpseo_content_score":null,"_yoast_wpseo_focuskeywords":null,"_yoast_wpseo_keywordsynonyms":null,"_yoast_wpseo_estimated-reading-time-minutes":null,"rank_math_description":null,"surfer_last_post_update":null,"surfer_last_post_update_direction":null,"surfer_keywords":null,"surfer_location":null,"surfer_draft_id":null,"surfer_permalink_hash":null,"surfer_scrape_ready":null,"_thumbnail_id":"18866","footnotes":null,"_links":{"self":[{"href":"https:\/\/webhosting.de\/en\/wp-json\/wp\/v2\/posts\/18873","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/webhosting.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/webhosting.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/webhosting.de\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/webhosting.de\/en\/wp-json\/wp\/v2\/comments?post=18873"}],"version-history":[{"count":0,"href":"https:\/\/webhosting.de\/en\/wp-json\/wp\/v2\/posts\/18873\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/webhosting.de\/en\/wp-json\/wp\/v2\/media\/18866"}],"wp:attachment":[{"href":"https:\/\/webhosting.de\/en\/wp-json\/wp\/v2\/media?parent=18873"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/webhosting.de\/en\/wp-json\/wp\/v2\/categories?post=18873"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/webhosting.de\/en\/wp-json\/wp\/v2\/tags?post=18873"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}