...

MariaDB Page Cleaner Threads verstehen: So beeinflussen sie die Performance

Page Cleaner Threads in MariaDB steuern, wie InnoDB geänderte Seiten aus dem Buffer Pool auf Datenträger schreibt und damit Antwortzeiten unter Schreiblast glättet. Wer die aktuelle Architektur mit einem einzelnen Cleaner-Thread versteht, vermeidet Engpässe im Schreibpfad und hält die database performance gleichmäßig.

Zentrale Punkte

  • Architektur: Ein Cleaner-Thread flush’t Dirty Pages unabhängig von Buffer-Pool-Instanzen.
  • Versionen: Die Variable innodb_page_cleaners entfiel ab MariaDB 10.6.
  • LRU-Fokus: Flush-Auswahl orientiert sich an LRU-Ende und Checkpoint-Fortschritt.
  • Mythos: Mehr Threads bedeuten nicht automatisch bessere Leistung.
  • Praxis: Größe des Buffer Pools, I/O-Kapazität und Checkpointing dominieren das Ergebnis.

Was der Page Cleaner genau tut

Der Page-Cleaner-Thread schreibt Dirty Pages aus dem InnoDB Buffer Pool zurück, bevor Benutzervorgänge hart auf den Datenträger treffen. Dadurch entkoppelt er Schreibarbeit von Abfragen und senkt spürbar die Varianz bei Antwortzeiten, vor allem während Lastspitzen. Ich sehe den Cleaner als Taktgeber: Er portioniert Writes in passende Häppchen, statt große Wellen unkontrolliert abzuarbeiten. Der Thread bedient sich an Seiten, die am Ende der LRU-Liste landen, damit der Cache zügig wieder frei für heiße Daten bleibt. Gleichzeitig treibt er den Checkpoint voran, sodass nicht zu viele ungeschriebene Änderungen im Speicher hängen bleiben. Wer diesen Ablauf versteht, erkennt schneller, ob I/O das Nadelöhr ist oder ob der Engpass eher aus zu kleinem Cache und zu vielen Dirty Pages resultiert.

Versionsstand: Von vielen Threads zu einem

Historisch ließen sich mehrere Cleaner konfigurieren, doch MariaDB 10.5.1 leitete den Umbau ein und MariaDB 10.6 entfernte innodb_page_cleaners endgültig. Seitdem erledigt ein einzelner buf_flush_page_cleaner-Thread die Arbeit für alle Buffer-Pool-Instanzen. Das reduziert Koordinationskosten, vereinfacht Tuning und spiegelt die Erkenntnis wider, dass ein guter Algorithmus wichtiger ist als Thread-Vielfalt. Wer Anleitungen aus MySQL- oder Alt-Artikeln übernimmt, stolpert schnell über Parameter, die heute wirkungslos sind. Ich prüfe zuerst die genaue MariaDB-Version, bevor ich vermeintliche Stellschrauben setze. So vermeide ich Zeitverlust und konzentriere mich auf die Stellgrößen, die den Schreibpfad wirklich beeinflussen.

Buffer Pool, Dirty Pages und LRU

Der Buffer Pool hält heiße Daten im RAM und spart teure Disk-Zugriffe ein. Sobald Transaktionen schreiben, entstehen Dirty Pages, die zunächst nur im Speicher existieren. Der Cleaner schreibt sie rechtzeitig weg, damit die LRU am Ende frei wird und häufig gelesene Seiten oben im Cache bleiben. Ich achte darauf, wie viele Buffer-Pool-Instanzen aktiv sind und wie sich der Zugriff verteilt, denn Parallelität kann Warteschlangen entschärfen. Wer tiefer einsteigen will, findet praktische Hinweise zu Buffer Pool Instanzen, etwa für Multicore-Hosts. Am Ende zeigt die Dirty-Page-Quote, ob die Flush-Kadenz mit der Schreibrate Schritt hält und ob der Cache seine Treffer liefert.

Checkpoint-Fortschritt und Latenz

Der Checkpoint setzt einen Marker, bis zu dem Änderungen sicher auf Datenträger liegen, und der Page Cleaner schiebt diesen Marker vorwärts. Bleibt der Checkpoint zurück, wachsen Log-Nutzungsgrad und Write-Amplification, was sich in Commit-Dauer und Spitze-n bei Abfragen zeigt. Ich prüfe regelmäßig, wie stark die Checkpoint-Distanz schwankt und ob der Cleaner zu großen Wellen greift. Gelingt die Glättung nicht, drohen Stoßzeiten, in denen Nutzer-Threads blockieren. Für das Grundverständnis hilft ein Blick auf Checkpointing und Write Amplification im Hosting-Kontext. Wer diese Kennzahlen liest, erkennt früh, ob Flush-Arbeit rechtzeitig erledigt wird oder ob das System in späteren Phasen hektisch aufholt.

Typische Missverständnisse beim Tuning

Viele erwarten, dass zusätzliche Hintergrund-Threads automatisch mehr Durchsatz liefern, doch das trifft hier nicht zu. Entscheidend bleibt die Qualität des Flush-Algorithmus und die passende Dosis an I/O-Arbeit pro Intervall. Ein zu aggressiver Cleaner erzeugt kurze Lastspitzen, die Antwortzeiten nach oben treiben. Ein zu zahmer Cleaner sammelt zu viele Dirty Pages an, wodurch später größere Flush-Wellen entstehen. Beides fühlt sich an wie ein Ziehharmonika-Effekt bei Latenzen. Ich ziele deshalb auf ein gleichmäßiges Muster, das zum Speichersubsystem passt und Nutzer-Threads möglichst wenig blockiert.

Metriken und Monitoring: Was ich prüfe

Für Entscheidungen verlasse ich mich auf Zahlen, nicht auf Bauchgefühl. Ich beobachte Dirty-Page-Anteil, Checkpoint-Fortschritt, Write- und Fsync-Raten sowie Wartezeiten auf Redo-Log und Datafiles. Wenn Commit-Zeiten unter Last schwanken, werfe ich einen Blick auf Flush-Backlogs und die Größe der Redo-Log-Dateien. Auch der Anteil an Seiten am LRU-Ende sagt etwas über Eviktionsdruck und den Bedarf an Flush-Arbeit. Ausreißer bei IOPS zeigen, dass der Cleaner zu große Pakete schreibt oder dass das Storage-Limit erreicht ist. Diese Messpunkte decken auf, ob der Engpass eher Cache-Größe, Speicher-Durchsatz oder Flush-Strategie betrifft.

Konfiguration: Größen und I/O-Kapazität richtig wählen

Die wichtigsten Stellschrauben bleiben Buffer-Pool-Größe, I/O-Kapazität und Log-Layout. Ein größerer Buffer Pool reduziert Lesedruck, darf aber den Dirty-Page-Anteil nicht hemmungslos wachsen lassen. Die Parameter zur I/O-Kapazität steuern, wie viel der Cleaner in einer Zeiteinheit versucht zu schreiben. Zu kleine Werte führen zu Rückstau, zu große erzeugen Zacken im Latenzprofil. Ich passe diese Größen an das reale Datenträgersystem an, statt abstrakten Standardwerten zu vertrauen. Die folgende Tabelle fasst relevante Einstellungen zusammen, die das Verhalten des Flush-Prozesses prägen.

Einstellung/Aspekt Wirkung auf Page Cleaner Hinweis für MariaDB Praktischer Anhaltspunkt
innodb_buffer_pool_size Beeinflusst Dirty-Page-Menge und Eviktionsdruck Größerer Pool braucht konsistente Flush-Kadenz RAM nutzen, aber Reserve für OS und Query-Cache lassen
innodb_io_capacity / innodb_io_capacity_max Begrenzt Umfang geplanter Flush-Arbeit An reale IOPS von SSD/NVMe anpassen Mit konservativem Wert starten, dann schrittweise erhöhen
innodb_flush_log_at_trx_commit Steuert Commit-Fsync-Häufigkeit Wahl beeinflusst Latenz und Haltbarkeit „1“ für höchste Haltbarkeit; „2/0“ für niedrigere Latenz
Redo-Log-Größe Wirkt auf Checkpoint-Distanz und Flush-Wellen Zu klein erzwingt häufige Checkpoints Größer dimensionieren, um Write-Spitzen zu glätten
innodb_page_cleaners (alt) Heute ohne Einfluss Ab MariaDB 10.6 entfernt Nicht mehr anfassen, Fokus auf aktive Parameter

Praxisleitfaden: Schritt-für-Schritt testen

Ich beginne mit einer klaren Baseline unter Last, bevor ich Einstellungen ändere. Danach reguliere ich innodb_io_capacity in kleinen Schritten und beobachte, ob Latenzspitzen seltener auftreten. Zeigen sich längere Flush-Wellen, erhöhe ich die Redo-Log-Größe, damit der Checkpoint mehr Pufferraum erhält. Anschließend prüfe ich, ob der Buffer Pool genug Platz hat, damit heiße Daten nicht zu schnell verdrängt werden. Jede Änderung bekommt ausreichend Zeit, damit sich Wirkung und Nebenwirkung sauber zeigen. Erst wenn Kennzahlen und Nutzererlebnis zusammen besser werden, hake ich den Schritt ab.

Einfluss des Doublewrite-Puffers

Der Doublewrite-Puffer schützt Seiten vor Teilwrites und korrumpierten Blöcken, wirkt aber zugleich auf Schreibrate und Flush-Muster. Gerade bei hohem Update-Anteil kann er den wahrgenommenen Durchsatz des Cleaners beeinflussen. Moderne Storage-Systeme mit persistenter Schreibreihenfolge entschärfen einiges, dennoch bleibt der Effekt messbar. Ich prüfe daher Workload, Datenintegritätserwartungen und akzeptable Latenz, bevor ich an dieser Schraube drehe. Wer Details dazu benötigt, findet Hintergründe im Artikel zum Doublewrite-Puffer. So lässt sich entscheiden, ob Lebensdauer und Schutz Vorrang vor minimalster Latenz erhalten.

Häufige Symptome und Gegenmaßnahmen

Springen Commit-Zeiten nach oben, obwohl CPU frei ist, spricht das für Flush-Stau oder schwaches Storage. Hohes Auf und Ab bei IOPS deutet auf zu große Flush-Pakete hin; dann reguliere ich die I/O-Kapazität nach unten und vergrößere das Redo-Log. Bleibt der Dirty-Page-Anteil dauerhaft hoch, arbeitet der Cleaner zu defensiv oder der Buffer Pool ist zu klein. Rutschen viel genutzte Seiten schnell an das LRU-Ende, fehlt Cache-Platz oder die Schreiblast presst den Pool zu stark aus. In Hosting-Umgebungen bremst oft das geteilte Storage; hier hilft nur Lastmessung über den Tag und gegebenenfalls ein Wechsel auf schnellere Medien. Ich dokumentiere jede Änderung, damit Ursache und Wirkung später eindeutig bleiben.

Wie der Cleaner zwischen Flush-List und LRU priorisiert

InnoDB unterscheidet beim Schreiben zwischen zwei Hauptquellen: der LRU-Liste (Seiten, die für neue Zugriffe Platz machen müssen) und der Flush-List (alle Dirty Pages, sortiert nach ältester Log-Sequenznummer). Der Page Cleaner balanciert diese beiden Ziele: Er räumt am LRU-Ende auf, um Eviktionen zu vermeiden, und zieht parallel aus der Flush-List, um den Checkpoint konstant voranzuschieben. Gerät der freie Pufferbestand unter Druck, hat LRU-Flush Priorität; schwillt dagegen die Checkpoint-Distanz an, erhöht der Cleaner den Anteil aus der Flush-List. Dieses Umschalten erklärt, warum Latenzprofile sich bei wechselnden Workloads verändern: Steigt Lesedruck, dominieren LRU-Flushes; steigt Schreibdruck, dominiert die Checkpoint-Arbeit. Ich lese das Muster im Monitoring, um zu entscheiden, ob ich eher I/O-Kapazität oder Redo-Log-Reserve nachschärfen muss.

Adaptive Flushing: Schwellwerte richtig deuten

MariaDB setzt auf adaptives Flushing, um die Schreibrate dynamisch an Redo-Verbrauch und Dirty-Page-Anteil anzupassen. In der Praxis beobachte ich drei Größen: den Zielwert für Dirty Pages, die Low-Water-Marke und die momentane Schreibrate. Liegt die Dirty-Page-Quote über dem Soll, zieht der Cleaner die Zügel an; fällt sie darunter, wird er zurückhaltender. Eine zu niedrige Low-Water-Marke führt zu häufigem Anlaufen des Flushs und kann kurze, aber spürbare Latenzspitzen erzeugen. Eine zu hohe Marke lässt zu viel Dreck im Speicher stehen, was später größere Wellen produziert. Ich justiere die Schwellwerte so, dass sie dem Charakter des Speichersystems entsprechen: schnelle NVMe-SSDs verkraften kontinuierliche, moderat höhere Flush-Raten; trägere Systeme profitieren von glatteren, kleineren Batches.

Storage-spezifische Optionen sinnvoll einsetzen

Der Page Cleaner arbeitet nicht im luftleeren Raum – die Wahl der Flush-Methode und das Verhalten des Dateisystems prägen das Ergebnis. Mit innodb_flush_method steuere ich, ob InnoDB Seiten direkt (O_DIRECT) oder über den OS-Cache schreibt. Direktes Schreiben vermeidet Doppel-Caching und stabilisiert Latenzen auf Linux mit XFS/EXT4. Dateisysteme wie ZFS handhaben O_DIRECT jedoch anders; dort prüfe ich, ob eine synchronisierte Methode (fsync/O_DSYNC) das konsistentere Profil liefert. Zusätzlich lohnt ein Blick auf das Nachbarschafts-Flushen (flush neighbors): Auf HDD-Arrays kann das Mitschreiben benachbarter Blöcke sinnvoll sein, auf SSD/NVMe reduziere ich es, um unnötige Write-Amplification zu vermeiden. Entscheidend ist, dass die Konfiguration zum physikalischen Medium passt – der beste Cleaner-Algorithmus nützt wenig, wenn das darunterliegende Storage ausgebremst wird.

Monitoring in der Praxis: Abfragen, die mir helfen

Für den schnellen Überblick nutze ich drei Blickwinkel: globale Statuswerte, InnoDB-Metriken und den periodischen Dump.

  • Schnelle Kennzahlen: SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_dirty%';, ... LIKE 'Innodb_os_log_written';, ... LIKE 'Innodb_log_waits';. Steigen log waits, ist das Redo-Log zu klein oder Flush zu träge.
  • Detailtiefe: SHOW ENGINE INNODB STATUS\G liefert Checkpoint-Positionen (LSN), Flush-List-Längen und Hinweise auf Engpässe. Ich vergleiche „Log sequence number“ und „Last checkpoint at“, um die Checkpoint-Distanz zu schätzen.
  • Feinere Telemetrie: SELECT NAME, COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME LIKE 'buffer_%dirty%'; oder ... LIKE 'log_%'; zeigt Trends, die in Kurztests leicht übersehen werden.

Wichtig ist die Korrelation: Springen Commit-Latenzen zeitgleich mit erhöhter Fsync-Rate, ist der Cleaner vermutlich zu scharf eingestellt. Wachsen Dirty-Page-Quote und Checkpoint-Distanz gemeinsam, fehlt Flush-Durchsatz oder das Redo-Log ist zu knapp bemessen.

Workload-Profile: OLTP, Reporting, Bulk

Je nach Arbeitslast setze ich unterschiedliche Akzente. In OLTP-Umgebungen ziele ich auf stetige, kleine Flush-Batches und ein enges Latenzband – hier sind moderat eingestellte innodb_io_capacity und genügend Redo-Puffer entscheidend. Für Reporting- oder ETL-Fenster dulde ich zeitweise höhere Flush-Raten, achte aber darauf, dass sie nicht bis in die Nutzerspitzen auslaufen. Bei Massendatenladen bevorzuge ich größere Redo-Logs und – falls Haltbarkeitsanforderungen es erlauben – eine temporär reduzierte Fsync-Disziplin (innodb_flush_log_at_trx_commit=2). Der Page Cleaner kann dann kontinuierlich „hinterherarbeiten“, ohne Nutzer-Transaktionen auszubremsen. Nach Abschluss stelle ich die strengeren Werte wieder her, damit das Tagesgeschäft stabil bleibt.

Langläufer, Purge und indirekte Effekte

Auch wenn der Purge-Thread andere Ziele verfolgt (Aufräumen historischer Versionen), beeinflusst sein Tempo das Gesamtbild. Bleiben alte Versionen lange liegen, wächst der Platzbedarf und die Speicher- wie I/O-Last verteilen sich ungünstiger. Das kann den Page Cleaner indirekt belasten, weil mehr Seiten im Pool gebunden sind und die LRU schneller unter Druck gerät. Ich halte deshalb die Purge-Lags im Blick und sorge dafür, dass keine Langläufer-Transaktionen das System „festhalten“. Stabiler Purge-Fortschritt, kontinuierlicher Cleaner, ausgeglichene Write-Kadenz – diese drei Zahnräder müssen ineinandergreifen.

Checkliste zur Fehlersuche im Schreibpfad

  • Checkpoint-Distanz hoch und steigend? Redo-Log vergrößern und innodb_io_capacity anheben, dann Verlauf erneut prüfen.
  • IOPS-Zacken und Commit-Spitzen? innodb_io_capacity leicht senken, Batch-Größe glätten, Doublewrite-Effekt berücksichtigen.
  • Dirty-Page-Anteil dauerhaft hoch? Buffer Pool vergrößern oder adaptives Flushing straffer stellen; Workload auf Hotsets prüfen.
  • Log-Waits sichtbar? Entweder Redo zu klein oder Flush hinkt. Erst Redo-Reserve erhöhen, dann Cleaner-Durchsatz feinjustieren.
  • LSN-Fortschritt unruhig? Flush-Pakete sind inkonsistent. Werte schrittweise ändern, bis regelmäßiger Fortschritt sichtbar wird.
  • Storage-nahe Engpässe? Flush-Methode, Scheduler und RAID-/SAN-Cache-Einstellungen validieren; nachhaltige statt Peak-IOPS als Zielgröße verwenden.

Beispiel: Kalibrierung in drei Runden

In einer schreiblastigen OLTP-Instanz starte ich mit einer Lastmessung im Produktionsfenster. Runde 1: Ich messe die Redo-Log-Füllstände und die Checkpoint-Distanz. Das Log ist häufig zu 70–80 % belegt, die Distanz schwankt stark – also verdopple ich die Redo-Größe. Runde 2: Nach erneutem Test glätten sich die Latenzen, aber gelegentlich schlagen Fsync-Spitzen durch. Ich senke innodb_io_capacity moderat, bis die IOPS-Verteilung ruhiger wird. Runde 3: Die Dirty-Page-Quote bleibt an der oberen Kante. Ich gebe dem Buffer Pool mehr RAM, was die LRU entlastet und die Cleaner-Arbeit planbarer macht. Ergebnis: Commit-P95 sinkt spürbar, die IOPS-Kurve wird gleichmäßiger und der Checkpoint marschiert konstant vorwärts – genau das Muster, das ich anstrebe.

Kurz zusammengefasst

Ein einzelner Cleaner-Thread organisiert das Flushen von Dirty Pages, hält den Checkpoint in Bewegung und schützt Abfragen vor harten Schreibspitzen. Relevante Hebel bleiben Buffer-Pool-Größe, I/O-Kapazität, Redo-Log-Layout und die Charakteristik des Speichersystems. Veraltete Stellschrauben wie innodb_page_cleaners beachte ich nicht mehr und konzentriere mich auf Kennzahlen mit direktem Einfluss. Wer Metriken wie Dirty-Page-Quote, Checkpoint-Abstand und Commit-Dauer liest, findet Engpässe schneller. Schrittweise Änderungen mit klarer Baseline liefern verlässliche Ergebnisse, ohne Seiteneffekte zu verstecken. So arbeitet der Page Cleaner leise im Hintergrund, und die Antwortzeit bleibt gleichmäßig – auch unter Last.

Aktuelle Artikel