Ich zeige, wie linux dirty und dirty background ratio den Page-Cache steuern und damit Schreibdurchsatz, Latenz und Datensicherheit formen. So legst du konkrete Grenzwerte fest, die Flusher rechtzeitig starten, Blockierungen vermeiden und die Schreibleistung deiner Workloads steigern.
Zentrale Punkte
Zum Einstieg fasse ich die Kernaussagen kurz zusammen, bevor ich tiefer einsteige.
- Dirty pages puffern Writes im RAM und bündeln viele kleine Zugriffe zu effizienteren I/O-Operationen.
- dirty_background_ratio startet Flusher-Threads im Hintergrund und begrenzt so unbemerkt die Schmutzmenge.
- dirty_ratio bremst schreibende Prozesse, wenn der harte Grenzwert überschritten wird.
- Relation beider Werte entscheidet über Latenzspitzen, Durchsatz und Puffergröße.
- Bytes-Varianten (dirty_bytes) geben auf großen Servern feinere, absolute Steuerung.
Dirty Pages verstehen
Wenn ein Prozess Daten schreibt, landen sie zuerst im Page-Cache und werden als „dirty“ markiert, bis der Kernel sie auf den Datenträger flushen kann. Dieses Puffern beschleunigt Anwendungen, weil RAM schneller als jede SSD oder HDD reagiert und kleine Writes zu großen, sequentiellen Transfers zusammenwachsen. Ich halte mir dabei stets vor Augen, wie viel „Schmutz“ ich zulasse, denn zu viel Puffer kann Warteschlangen verlängern oder bei Abstürzen mehr ungesicherte Daten riskieren. Wer die Funktionsweise versteht, trifft bessere Entscheidungen für Writeback, Latenz und Speicherdruck. Ein kurzer Hintergrundartikel zum Writeback-Cache hilft, diese Mechanik sauber einzuordnen.
Datensicherheit, fsync und Crashfenster
Die Grenzwerte beeinflussen nicht nur Leistung, sondern auch dein Risiko-Fenster. Ich überschlage es mit einer einfachen Faustformel: maximal unsichere Datenmenge geteilt durch nachhaltigen Gerätedurchsatz ergibt grob die Dauer, bis der Puffer leer ist. Beispiel: Erlaube ich 4 GB Dirty-Daten und erreicht das Zielmedium 500 MB/s, dauert die vollständige Rückschreibung rund 8 Sekunden. In dieser Zeit können bei Stromausfall oder Kernel-Panik die jüngsten Writes fehlen.
Anwendungen können das Fenster durch fsync() oder fdatasync() verkleinern, denn diese Aufrufe zwingen das Dateisystem, Daten (und je nach Journaling-Modus auch Metadaten) auf das Medium zu bringen. Das ist teurer, aber essenziell für Datenbanken oder Journals. Ich achte darauf, dass meine Dirty-Grenzen zum Sync-Verhalten passen: Häufige fsync()-Aufrufe profitieren von niedrigerem dirty_ratio, damit der Kernel nicht zusätzlich drosselt, wenn ohnehin regelmäßig persistiert wird. Umgekehrt darf ich bei append-lastigen Logs, die selten flushen, höhere Puffer erlauben – immer mit Blick auf das akzeptierte Verlustrisiko.
Wichtig sind auch Barrieren und Schreibreihenfolgen: Moderne Dateisysteme nutzen FUA/Flush-Befehle, um Controller-Caches korrekt zu leeren. Auf Medien ohne Power-Loss-Protection verlängern große Puffer das Risiko; mit PLP oder Schreibcache-Absicherung sind größere Puffer oft vertretbar.
Dirty Background Ratio: der weiche Schwellwert
Mit dirty_background_ratio gebe ich an, ab welchem Prozentsatz des verfügbaren Speichers Flusher-Threads beginnen, im Hintergrund zu schreiben. Dieser Wert blockiert keine Anwendungen, sondern startet still die Aufräumarbeiten, damit der Puffer nicht ausufert. Niedrige Zahlen erzeugen häufigeres, aber gleichmäßigeres Hintergrundschreiben und glätten Latenzspitzen. Höhere Zahlen lassen mehr Puffern zu, was bei großen sequentiellen Writes den Durchsatz hebt, jedoch bei plötzlichem Flush beachtliche I/O-Spitzen auslösen kann. Typisch liegen Defaults um zehn Prozent, doch ich verschiebe die Grenze je nach Medium, Workload und Sicherheitsanspruch.
Dirty Ratio: die harte Bremse
Der Parameter dirty_ratio markiert die Linie, an der der Kernel schreibende Prozesse drosselt, bis genug Seiten zurückgeschrieben sind. Diese harte Grenze schützt den Speicher vor einer Flut nicht persistierter Daten und wirkt damit direkt auf Anwendungen, sobald sie weiter produzieren wollen. Bei Datenbanken setze ich den Wert eher niedrig, damit Anfragen konstante Antwortzeiten behalten und keine langen Flush-Phasen entstehen. Für Backup-Jobs nutze ich dagegen großzügigere Puffer, um große Blöcke effizient zu übertragen. Übliche Defaults variieren zwischen zwanzig und vierzig Prozent, doch ich passe diesen Bereich immer an die konkrete Last an.
Zusammenspiel und typische Relationen
Beide Grenzwerte arbeiten als Tandem und entfalten ihre Wirkung erst gemeinsam. Ich halte dirty_background_ratio stets geringer als dirty_ratio, damit der Kernel rechtzeitig im Hintergrund startet und die harte Bremse selten greift. Als Faustgröße wähle ich oft ein Viertel bis die Hälfte der harten Grenze, also beispielsweise 5–10 zu 20. So läuft Writeback ausreichend früh an, ohne den Durchsatz unnötig zu drücken. Wer diese Relation verfehlt, erlebt entweder zu frühe Drosselung oder zu späte Hintergrundarbeit mit spürbaren Latenzspitzen.
Per-Device-Steuerung und Block-Layer-Feinheiten
Neben den globalen Limits lohnt sich ein Blick auf die Geräteebene. Linux verteilt Dirty-Last über sogenannte Backed Devices (bdi). In /sys/class/block/<dev>/bdi/ finde ich Parameter wie max_ratio, die bestimmen, wie viel vom global erlaubten Dirty-Budget ein einzelnes Gerät ziehen darf. Auf Systemen mit langsamen und schnellen Laufwerken parallel begrenze ich langsame Datenträger, damit sie nicht zum Nadelöhr werden.
Ebenfalls relevant ist die Block-Layer-Drosselung über /sys/block/<dev>/queue/wbt_lat_usec (Writeback Throttling). Damit peile ich eine Ziel-Latenz an; der Kernel drosselt dann schreibende Last, wenn sie diese Zielzeit reißt. Bei SATA-HDDs setze ich gern konservative Werte, um Interaktivität zu schützen. Auf sehr schnellen NVMe-Laufwerken deaktiviere oder erhöhe ich die Ziel-Latenz, damit der Controller seine Parallelität ausspielen kann. Den I/O-Scheduler (mq-deadline, BFQ, none) wähle ich passend: BFQ hilft bei interaktiven Systemen mit gemischter Last, während none oder mq-deadline bei reinen Durchsatzjobs auf NVMe oft am besten läuft.
Das Zusammenspiel ist entscheidend: Ist dirty_background_ratio niedrig, aber das Gerät per WBT aggressiv limitiert, entstehen trotzdem sichtbare Staus. Ich kalibriere daher beide Ebenen gemeinsam – globale Dirty-Limits für Puffergröße, Block-Layer für Latenzschutz.
Ratio vs. Bytes: Default-Werte und Varianten
Auf Systemen mit viel RAM wachsen Prozentwerte schnell zu großen absoluten Größenordnungen an. Dann führe ich lieber absolute Obergrenzen mit dirty_bytes und dirty_background_bytes ein, um etwa 2–8 GB Puffermenge klar zu limitieren. Das entkoppelt die Regelung von stark schwankenden Speicherausbau-Stufen und hält die Menge nicht persistierter Daten kalkulierbar. Die Wahl bleibt dynamisch: Für kleine Server mit wenig RAM reichen Prozentsätze oft völlig aus. Wer hohe Kapazität besitzt, fährt mit Bytes-Werten oft planbarer.
| Parameter | Bedeutung | Typische Defaults | Wann ändern? | Hinweis |
|---|---|---|---|---|
| vm.dirty_background_ratio | Start des Hintergrund-Flush in Prozent | ≈ 10% | Bei Latenzschwankungen oder sehr schnellen SSD/NVMe | Niedriger = glattere Latenz, höher = mehr Puffer |
| vm.dirty_ratio | Harte Drossel-Grenze in Prozent | ≈ 20–40% | Bei Datenbanken niedriger, bei Backups höher | Zu hoch → Blockierungen bei Flush möglich |
| vm.dirty_background_bytes | Start des Hintergrund-Flush in Bytes | Deaktiviert, wenn Ratio genutzt | Großer RAM, feste Pufferziele | Setzt Ratio-Parameter außer Kraft |
| vm.dirty_bytes | Harte Drossel-Grenze in Bytes | Deaktiviert, wenn Ratio genutzt | Großer RAM, planbare Obergrenze | Setzt Ratio-Parameter außer Kraft |
Workload-Szenarien und Empfehlungen
Sequenzielle Schreiblasten wie Backups profitieren von großen Puffern und moderatem Hintergrundschreiben, weil der Kernel in großen Zügen auf das Medium schreiben kann. Ich setze hier dirty_ratio oft zwischen 30 und 40 Prozent und dirty_background_ratio zwischen 10 und 20 Prozent. Datenbanken und kleine Random-I/O-Anwendungen leben von vorhersehbarer Latenz, daher wähle ich 10–15 Prozent hart und 3–5 Prozent weich. Für gemischte Web- und App-Server erweisen sich 15–20 Prozent hart und 5–10 Prozent weich als guter Kompromiss. Diese Spannen gelten als Startpunkt, danach zählt die gemessene Realität deines Systems.
Dateisystem-Aspekte und Mount-Optionen
Der Writeback-Pfad endet im Dateisystem – dessen Strategie formt Latenzen und Sicherheit. Ext4 mit data=ordered (Default) schreibt Nutzdaten vor Journal-Committs; data=writeback senkt Latenz, riskiert aber alte Daten nach Crashes. Der Parameter commit= (Sekunden) steuert, wie häufig das Journal persistiert wird. Kürzere Intervalle verringern Datenverlust, kosten aber mehr I/O. XFS nutzt ein ausgereiftes Log-Design; große logbsize und adäquates Alignment helfen Durchsatzjobs. Btrfs bündelt Writes durch Copy-on-Write – das stabilisiert Latenzen, kann aber bei kleinen Random-Writes und knappen SSDs zu Fragmentierung führen. Optionen wie nodatacow für bestimmte Pfade oder gezielte Defragmentierung helfen, wenn Latenzspitzen auftreten.
Ich beachte zudem relatime/noatime (reduziert Metadaten-Writes), lazytime (verzögert mtime/atime persistenter), und Journaling-Barrieren. Gerade auf RAID-Controllern oder in VMs ist die korrekte Cache-Semantik entscheidend: Falsch gesetzte Schreibcaches machen jede Dirty-Tuning-Arbeit zunichte.
Direct I/O, O_SYNC und Anwendungsverhalten
Nicht jede Anwendung durchläuft den Page-Cache. Mit O_DIRECT oder O_SYNC/O_DSYNC umgehen Prozesse Teile des Caches oder verlangen sofortige Persistenz. Datenbanken schreiben typischerweise ein WAL/Redo-Log synchron und Datenbereiche asynchron. Ich kalibriere Dirty-Grenzen insbesondere für asynchrone Pfade, während ich synchronen Pfaden über schnelle Journals (NVMe, dedizierte LUN) Latenz garantiere. Wenn Anwendungen sehr häufig fsync() aufrufen, sind große Puffer wenig hilfreich – die Latenz hängt dann stärker von Controller, Queue-Depth und I/O-Scheduler ab als von dirty_ratio.
Praktisches Tuning Schritt für Schritt
Vor jeder Änderung prüfe ich die Ist-Werte mit sysctl vm.dirty_ratio und sysctl vm.dirty_background_ratio, um die Ausgangslage zu dokumentieren. Für kurzfristige Tests schreibe ich Werte direkt nach /proc/sys/vm/, zum Beispiel echo 15 > /proc/sys/vm/dirty_ratio und echo 5 > /proc/sys/vm/dirty_background_ratio. Bleibt die Anpassung dauerhaft, sichere ich sie in /etc/sysctl.conf oder /etc/sysctl.d/*.conf. Änderungen setze ich mit sysctl -p unmittelbar um, damit ich den Effekt zeitnah messe. Wer tiefer in das Thema Systemregeln einsteigt, profitiert von praxisnahen Hinweisen zum Sysctl-Tuning auf produktiven Servern.
Werte herleiten: Rechenbeispiele
Ich starte gern mit greifbaren Größen. Beispiel 1: Web-/App-Server mit 64 GB RAM, NVMe. Ziel ist glatte Latenz. Ich setze dirty_background_bytes=1073741824 (1 GB) und dirty_bytes=3221225472 (3 GB). Bei 2 GB/s nachhaltigem NVMe-Durchsatz bedeutet das ca. 0,5–1,5 Sekunden zum Leeren – gut für interaktive Last. Beispiel 2: Backup-Knoten mit 128 GB RAM, schnelles SATA-RAID mit 800 MB/s. Ich wähle dirty_background_ratio=10, dirty_ratio=35. Absolut sind das ca. 12,8 GB und 44,8 GB; das RAID braucht 16–56 Sekunden zum Leeren. Das ist ok, weil der Job nicht interaktiv ist.
Beispiel 3: Datenbankserver mit 256 GB RAM, getrenntes Journal auf NVMe, Daten auf SSD-Array. Ich limitiere absolut, um Ausreißer zu vermeiden: dirty_background_bytes=2147483648 (2 GB), dirty_bytes=8589934592 (8 GB). Das hält die Crash-Window-Größe planbar und reduziert harte Bremsen während Checkpoints.
Writeback-Timing und verwandte Parameter
Neben den Grenzwerten beeinflussen Timer das Writeback-Verhalten und damit das Gefühl der Anwendungen. Mit vm.dirty_writeback_centisecs steuere ich das Intervall, in dem der Kernel Flusher aufweckt, während vm.dirty_expire_centisecs definiert, wie alt dirty pages maximal werden dürfen. Kürzere Intervalle sorgen für häufigere, aber kleinere Flushes, längere Intervalle sparen I/O-Aufrufe, riskieren jedoch größere Bündel. Ich justiere diese Werte nur, wenn Messungen echte Nachteile zeigen, etwa zu seltene Flushes auf schnellen NVMe-Laufwerken. Wer hier methodisch vorgeht, vermeidet Schaukeln zwischen zu eifriger und zu träger Writeback-Aktivität.
Monitoring und Feineinstellung
Nach Anpassungen beobachte ich kontinuierlich die Kennzahlen, um Erfolge und Nebenwirkungen sichtbar zu machen. In /proc/meminfo prüfe ich „Dirty“ und „Writeback“, um Pufferstände und aktive Flushes zu sehen. Werkzeuge wie iostat, sar oder atop zeigen mir Durchsatz, Warteschlangen und Latenztrends. Ein passender Einstieg in Metriken liefert dieser Beitrag zum I/O-Wait analysieren. Erst auf Basis dieser Daten reduziere oder erhöhe ich Grenzen in kleinen Schritten, damit keine unerwarteten Seiteneffekte auftreten.
Container, cgroups und faire Verteilung
In Container-Umgebungen teilen sich Workloads die gleichen Kernel-Mechanismen. Cgroup-Writeback sorgt dafür, dass Dirty-Pages einem Verursacher zugeordnet werden. Ich nutze die I/O-Controller der cgroups (blkcg), um Bandbreite oder IOPS je Container zu limitieren, wenn einzelne Tenants zu aggressiv puffern. Absolute Bytes-Grenzen auf Hostebene (dirty_bytes) verhindern, dass ein einzelner Gast das gesamte Dirty-Budget frisst. Ergänzend begrenze ich Speicher über memory.max, damit Writeback nicht erst bei globalem Druck reagiert. Das Ziel bleibt: Keine Gast-Last darf Host-weite Drosselungen des dirty_ratio erzwingen.
Hosting-Umgebungen und VMs
In Mehrmandanten-Setups und VMs achte ich auf Überbuchung von RAM und I/O, weil prozentuale Grenzen sich dort anders auswirken. Absolute Bytes-Grenzen können verhindern, dass einzelne Gäste zu viel Puffer aufbauen und Nachbarn ausbremsen. Ich berücksichtige Speicherdeduplizierung, Ballooning und Controller-Caches, weil sie Pufferungseffekte überlagern. Für Managed-Server zahlt es sich aus, wenn der Anbieter sinnvolle Defaults setzt, damit Kunden konstante Antwortzeiten erleben. Wer eigene Knoten betreibt, profitiert von sauber definierten Profileinstellungen je Workload-Klasse.
Häufige Missverständnisse und Stolpersteine
- „Mehr Puffer = immer mehr Durchsatz.“ Falsch bei random-lastigen Workloads oder Geräten mit kleiner Queue-Depth. Zu große Puffer erzeugen Flush-Bursts und Warteschlangen.
- „dirty_ratio beeinflusst Reads nicht.“ Indirekt doch: Aggressive Writeback-Phasen verdrängen Cache-Seiten und erhöhen Read-Latenzen.
- „Bytes und Ratio addieren sich.“ Nein. Setzt du Bytes-Varianten, heben sie die Ratio-Pendants auf. Eindeutig bleiben.
- „fsync() macht Dirty-Limits irrelevant.“ Nein. Häufige Syncs mindern zwar das Risiko-Fenster, doch der Rest der Last unterliegt weiterhin den Grenzwerten.
- „Ein schneller Datenträger löst alles.“ Nicht, wenn der Block-Layer drosselt (WBT) oder das Dateisystem suboptimal montiert ist.
- „Drop_caches ist ein Tuning-Tool.“ Das Leeren des Caches verzerrt Messungen und verschlimmert Latenzspitzen. In Produktion vermeide ich es.
Troubleshooting: typische Symptome und Gegenmittel
Häufen sich Latenzspitzen, setze ich zuerst den Hintergrundschwellwert niedriger, damit Flusher früher beginnen und große Schreibwellen seltener entstehen. Blockieren Anwendungen phasenweise, liegt die harte Grenze meist zu hoch oder das Medium schafft die entstehenden Flush-Bursts nicht. In solchen Fällen senke ich dirty_ratio, prüfe Readahead-Einstellungen und schaue auf Dateisystem-Journaling-Optionen. Bei sehr schneller NVMe-Hardware erhöhe ich schrittweise die Hintergrundschwelle, um den Durchsatz nicht künstlich zu deckeln. Nach jeder Änderung folge ich dem Messbild, nicht dem Bauchgefühl.
Kurzbilanz für die Praxis
Mit wenigen Stellschrauben beeinflusse ich, wie Linux Schreibdaten puffert, wann Flusher starten und wann der Kernel bremst. Dirty Background Ratio sorgt für leises Aufräumen, Dirty Ratio begrenzt den RAM-Verbrauch harter. Die Relation beider Werte entscheidet, ob dein System eher auf gleichmäßige Latenzen oder maximalen Durchsatz zielt. Ich dokumentiere Defaults, ändere in kleinen Schritten und werte Messungen konsequent aus. So entsteht eine Konfiguration, die Workload, Medium und Risiko vernünftig ausbalanciert und in der Praxis spürbar schneller wirkt.


