fsync-Strategien im Vergleich
AOF-Persistence protokolliert jede Schreiboperation als Kommando in einer Append-Only-Datei und erlaubt Redis damit deutlich engere Durability-Garantien als reine RDB-Snapshots. Die Wahl der appendfsync-Strategie zwischen always, everysec und no entscheidet direkt darüber, wie viele Millisekunden Latenz gegen wie viele Sekunden potenziellen Datenverlust getauscht werden.
Inhaltsverzeichnis
- 1. Was AOF-Persistence von RDB unterscheidet
- 2. AOF aktivieren: appendonly und die Grundkonfiguration
- 3. appendfsync im Detail: always, everysec, no
- 4. Das AOF-Dateiformat: Manifest, Basis- und Inkrement-Dateien
- 5. Der AOF-Rewrite-Prozess im Detail
- 6. Rewrite-Trigger konfigurieren und steuern
- 7. Performance-Auswirkungen von fsync-Strategien
- 8. AOF-Dateien prüfen und reparieren
- 9. Typische Fehler bei AOF-Persistence
- 10. Zusammenfassung
- 11. FAQ
1. Was AOF-Persistence von RDB unterscheidet
AOF-Persistence, kurz für Append-Only File, verfolgt einen fundamental anderen Ansatz als RDB-Snapshots: Statt periodisch den gesamten Datenbestand zu sichern, protokolliert AOF jede einzelne Schreiboperation als Kommando in einer fortlaufend wachsenden Datei. Ein SET key value wird als genau dieser Befehl an das Ende der AOF-Datei angehängt, ein LPUSH ebenso. Beim Neustart spielt Redis diese Kommandos in derselben Reihenfolge erneut ab und rekonstruiert so den exakten Endzustand.
Der zentrale Vorteil von AOF-Persistence liegt in der Granularität der Durability-Garantie: Während RDB-Snapshots ein Zeitfenster von Minuten offenlassen können, lässt sich mit AOF das Verlustfenster auf eine Sekunde oder sogar auf null reduzieren, abhängig von der gewählten fsync-Strategie. Der Preis dafür ist eine größere Dateigröße im Vergleich zu einem kompakten RDB-Snapshot und ein langsamerer Neustart bei sehr langen Kommando-Historien, sofern kein aktueller Rewrite vorliegt.
In der Praxis wird AOF-Persistence meist dort eingesetzt, wo Redis nicht nur als Cache, sondern als primäre Datenquelle fungiert, etwa für Warteschlangen, Zähler, Session-Stores mit strikten Anforderungen oder Rate-Limiting-Zustände, bei denen ein Verlust von Sekunden an Schreiboperationen geschäftlich relevant wäre. Die Kombination beider Mechanismen, AOF und RDB gemeinsam, wird im nächsten Artikel dieser Reihe im direkten Vergleich behandelt.
2. AOF aktivieren: appendonly und die Grundkonfiguration
AOF-Persistence wird über die Direktive appendonly yes in redis.conf aktiviert, standardmäßig ist sie deaktiviert. Nach der Aktivierung schreibt Redis alle Schreibkommandos zusätzlich zu den bestehenden RDB-Mechanismen in das AOF-Verzeichnis, das über appenddirname konfiguriert wird, üblicherweise ein Unterordner namens appendonlydir relativ zum Hauptverzeichnis dir. Seit Redis 7 besteht dieses Verzeichnis aus mehreren Dateien statt einer einzigen monolithischen AOF-Datei, dazu mehr im Abschnitt zum Dateiformat.
Wichtig ist, dass eine Aktivierung von AOF zur Laufzeit über CONFIG SET appendonly yes sofort einen initialen AOF-Rewrite auslöst, der den aktuellen Datenbestand als Basis-Datei schreibt, bevor neue Kommandos angehängt werden. Diese Aktivierung sollte niemals unter voller Last ohne vorherige Kapazitätsplanung erfolgen, weil der initiale Rewrite denselben fork-basierten Mechanismus wie BGSAVE nutzt und entsprechend Copy-on-Write-Speicher benötigt.
# redis.conf - Basic AOF configuration
# Enable append-only file persistence
appendonly yes
# Directory for AOF files (relative to "dir")
appenddirname "appendonlydir"
# Base filename prefix for AOF manifest and files
appendfilename "appendonly.aof"
# fsync strategy (see next section)
appendfsync everysec
3. appendfsync im Detail: always, everysec, no
Die Direktive appendfsync ist die wichtigste Stellschraube bei AOF-Persistence, weil sie exakt festlegt, wann Redis den Betriebssystem-Puffer der AOF-Datei per fsync() physisch auf den Datenträger zwingt. Ein write()-Systemaufruf allein garantiert nämlich noch keine Persistenz: Die Daten landen zunächst im Page-Cache des Kernels und könnten bei einem Stromausfall verloren gehen, bevor der Kernel sie selbstständig auf die Platte schreibt. fsync() erzwingt genau diesen physischen Schreibvorgang.
Mit appendfsync always ruft Redis fsync() nach jedem einzelnen Schreibkommando auf. Das liefert die stärkste Durability-Garantie, praktisch kein Datenverlust bei einem Absturz, kostet aber massiv Latenz, weil jede Schreiboperation auf den langsamsten Bestandteil des Systems wartet: den physischen Datenträger. Auf rotierenden Festplatten kann das die Durchsatzrate auf wenige hundert Operationen pro Sekunde limitieren, selbst auf schnellen NVMe-SSDs bleibt ein spürbarer Latenz-Overhead im niedrigen Millisekundenbereich pro Schreibvorgang.
Mit appendfsync everysec, dem empfohlenen Standardwert, führt ein separater Hintergrund-Thread fsync() maximal einmal pro Sekunde aus. Das begrenzt den potenziellen Datenverlust im Absturzfall auf eine Sekunde an Schreiboperationen, während die Latenz für den Client praktisch unbeeinflusst bleibt, weil write() selbst schnell ist und der teure fsync()-Aufruf asynchron im Hintergrund läuft. Mit appendfsync no überlässt Redis den Zeitpunkt des physischen Schreibens komplett dem Betriebssystem, was die beste Performance, aber auch das größte Verlustrisiko bedeutet, typischerweise 30 Sekunden oder mehr an Daten, abhängig von den Kernel-Dirty-Page-Einstellungen.
# redis.conf - appendfsync strategies compared
# Strongest durability: fsync after every write command
# Slowest option, use only when zero data loss is mandatory
appendfsync always
# Recommended default: fsync at most once per second
# Bounded loss window of ~1 second, minimal latency impact
appendfsync everysec
# No explicit fsync by Redis, kernel decides when to flush
# Fastest, largest loss window (often 30s+), rarely recommended
appendfsync no
4. Das AOF-Dateiformat: Manifest, Basis- und Inkrement-Dateien
Seit Redis 7 wurde das AOF-Dateiformat grundlegend überarbeitet und besteht nicht mehr aus einer einzigen wachsenden Datei, sondern aus mehreren Komponenten innerhalb des appendonlydir-Verzeichnisses. Eine Manifest-Datei, meist appendonly.aof.manifest, listet alle zugehörigen Dateien in korrekter Reihenfolge auf: eine Basis-Datei mit dem Suffix .base.rdb oder .base.aof, die den Zustand zum Zeitpunkt des letzten Rewrites im RDB- oder AOF-Format enthält, sowie eine oder mehrere Inkrement-Dateien mit dem Suffix .incr.aof, die alle seither ausgeführten Kommandos enthalten.
Dieses Multi-Part-Format löst ein praktisches Problem der alten Single-File-Architektur: Ein Rewrite musste früher die komplette Datei neu schreiben und dabei parallel neue Kommandos puffern, was bei sehr großen Datenmengen zu Speicherdruck führte. Mit dem neuen Format schreibt ein Rewrite lediglich eine neue Basis-Datei und startet eine neue, leere Inkrement-Datei, während die alte Inkrement-Datei erst nach erfolgreichem Abschluss gelöscht wird. Das reduziert das Risiko inkonsistenter Zustände bei einem Absturz während des Rewrites erheblich.
# Inspect the AOF directory structure (Redis 7+ multi-part format)
ls -la /var/lib/redis/appendonlydir/
# appendonly.aof.1.base.rdb <- base snapshot from last rewrite
# appendonly.aof.1.incr.aof <- incremental commands since rewrite
# appendonly.aof.manifest <- ordered list of active files
cat /var/lib/redis/appendonlydir/appendonly.aof.manifest
# file appendonly.aof.1.base.rdb seq 1 type b
# file appendonly.aof.1.incr.aof seq 1 type i
5. Der AOF-Rewrite-Prozess im Detail
Ohne Rewrite würde eine AOF-Datei unbegrenzt wachsen, selbst wenn derselbe Key hunderte Male überschrieben wird, weil jedes einzelne Kommando protokolliert wird. Der BGREWRITEAOF-Prozess löst dieses Problem, indem er analog zu BGSAVE per fork() einen Kindprozess erzeugt, der den aktuellen, kompakten Datenbestand als neue Basis-Datei schreibt, statt die komplette Kommando-Historie zu erhalten. Ein Hash, der zehnmal überschrieben wurde, erscheint in der neuen Basis-Datei nur noch mit seinem finalen Wert, nicht mit zehn einzelnen Kommandos.
Während der Kindprozess die neue Basis-Datei schreibt, puffert der Elternprozess weiterhin eingehende Schreibkommandos in eine neue Inkrement-Datei. Nach Abschluss des Rewrites im Kindprozess übernimmt Redis atomar die neue Basis-Datei zusammen mit der während des Rewrites gesammelten Inkrement-Datei und aktualisiert die Manifest-Datei entsprechend. Die alte Basis- und Inkrement-Datei werden erst danach gelöscht, was sicherstellt, dass bei einem Absturz während des Rewrites immer ein konsistenter, ladbarer Zustand existiert.
Wie bei BGSAVE gilt auch bei BGREWRITEAOF: Der Fork selbst kostet kurzzeitig CPU-Zeit für die Duplizierung der Seitentabelle, und Copy-on-Write erhöht den Speicherbedarf proportional zur Schreiblast während des Rewrites. Ein manueller Aufruf via redis-cli BGREWRITEAOF ist jederzeit möglich, etwa nach einer bekannten Phase intensiver Schreiblast, um die AOF-Größe proaktiv zu reduzieren, statt auf den automatischen Trigger zu warten.
| appendfsync | Max. Datenverlust | Latenz-Overhead | Empfehlung |
|---|---|---|---|
| always | ~0 (praktisch keiner) | Hoch, jeder Write wartet auf Disk | Nur bei zwingender Zero-Loss-Anforderung |
| everysec | Bis zu 1 Sekunde | Minimal, fsync im Hintergrund | Empfohlener Standard für die meisten Setups |
| no | 30s+ (Kernel-abhängig) | Sehr gering | Nur wenn Performance klar vor Durability geht |
6. Rewrite-Trigger konfigurieren und steuern
Redis löst einen automatischen AOF-Rewrite anhand zweier konfigurierbarer Schwellenwerte aus: auto-aof-rewrite-percentage definiert, um wie viel Prozent die AOF-Größe seit dem letzten Rewrite gewachsen sein muss, standardmäßig 100 Prozent, also eine Verdopplung. auto-aof-rewrite-min-size verhindert, dass bei sehr kleinen AOF-Dateien unnötig häufig ein Rewrite ausgelöst wird, standardmäßig 64 Megabyte als Untergrenze. Beide Bedingungen müssen zutreffen, bevor Redis automatisch BGREWRITEAOF aufruft.
Diese Schwellenwerte sind ein direkter Kompromiss zwischen Dateigröße und Rewrite-Frequenz: Ein niedriger Prozentsatz hält die AOF-Datei kompakt, verursacht aber häufigere Fork-Operationen mit entsprechendem CPU- und Speicher-Overhead. Ein hoher Prozentsatz reduziert die Rewrite-Frequenz, lässt die Datei aber zwischenzeitlich stärker anwachsen, was sowohl Speicherplatz auf der Platte als auch die Ladezeit beim Neustart erhöht, sofern kein aktueller Rewrite unmittelbar davor stattfand.
# redis.conf - AOF rewrite trigger thresholds
# Trigger a rewrite once the AOF has grown 100% since the last rewrite
auto-aof-rewrite-percentage 100
# But never rewrite below this absolute size (avoids rewrite storms
# on small, freshly started instances)
auto-aof-rewrite-min-size 64mb
# Prevent BGSAVE and BGREWRITEAOF from running simultaneously
# (both fork; running together doubles COW pressure)
no-appendfsync-on-rewrite no
7. Performance-Auswirkungen von fsync-Strategien
Die Wahl der fsync-Strategie wirkt sich nicht nur auf die Latenz einzelner Schreiboperationen aus, sondern auch auf den Gesamtdurchsatz unter Last. Bei appendfsync always wird jeder einzelne Schreibbefehl faktisch seriell durch einen physischen Disk-Write ausgebremst, was auf klassischen Festplatten wenige hundert Operationen pro Sekunde bedeutet und selbst auf schnellen NVMe-Laufwerken den Durchsatz gegenüber everysec spürbar reduziert, weil jeder Schreibvorgang auf den Abschluss des vorherigen fsync() warten muss.
Ein oft übersehener Parameter ist no-appendfsync-on-rewrite. Standardmäßig auf no gesetzt, führt Redis fsync-Aufrufe auch während eines laufenden AOF-Rewrites regulär weiter aus. Auf Systemen mit langsamem I/O kann das während eines Rewrites zu erhöhter Latenz führen, weil der Rewrite-Kindprozess und die regulären fsync-Aufrufe um dieselbe I/O-Bandbreite konkurrieren. Wird der Parameter auf yes gesetzt, pausiert Redis fsync-Aufrufe während des Rewrites komplett, was das Verlustrisiko im ungünstigsten Fall auf die Dauer des kompletten Rewrites erhöht, im Gegenzug aber Latenz-Spitzen während des Rewrites vermeidet.
In der Praxis zeigt sich, dass die meisten produktiven Redis-Setups mit everysec ein optimales Verhältnis zwischen Durability und Durchsatz erreichen, während always nur für sehr spezifische Compliance- oder Finanz-Anwendungsfälle mit expliziter Zero-Loss-Anforderung gerechtfertigt ist, bei denen die Latenzkosten bewusst in Kauf genommen werden.
8. AOF-Dateien prüfen und reparieren
Redis liefert mit redis-check-aof ein dediziertes Werkzeug, um AOF-Dateien auf Konsistenz zu prüfen, insbesondere nach einem harten Absturz, bei dem die letzte Schreiboperation möglicherweise unvollständig auf die Platte geschrieben wurde. Ein abgeschnittenes letztes Kommando am Dateiende ist bei AOF ein bekanntes und tolerierbares Szenario: Redis erkennt ein solches Trunkierungsproblem beim Start und kann es, sofern aof-load-truncated yes gesetzt ist, automatisch beheben, indem die unvollständige letzte Zeile verworfen wird.
Für tiefere Reparaturen ruft man redis-check-aof --fix auf die betroffene Inkrement-Datei auf. Das Werkzeug liest die Datei sequenziell, validiert jedes Kommando gegen das RESP-Protokollformat und schneidet die Datei am letzten vollständig validen Kommando ab. Wichtig: Vor jedem --fix-Aufruf sollte immer eine Kopie der Originaldatei angelegt werden, weil die Reparatur destruktiv arbeitet und im schlimmsten Fall mehr Daten verwirft, als tatsächlich beschädigt waren.
#!/usr/bin/env bash
# check_aof_integrity.sh - Validate AOF files before a risky restart
set -euo pipefail
AOF_DIR="/var/lib/redis/appendonlydir"
BACKUP_DIR="/var/backups/redis/aof-precheck-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
cp -a "$AOF_DIR"/. "$BACKUP_DIR/"
for incr_file in "$AOF_DIR"/*.incr.aof; do
echo "Checking: $incr_file"
if ! redis-check-aof "$incr_file"; then
echo "[WARN] Issues found in $incr_file, backup preserved at $BACKUP_DIR" >&2
fi
done
echo "[OK] AOF integrity check complete, backup at $BACKUP_DIR"
9. Typische Fehler bei AOF-Persistence
Ein häufiger Fehler ist, appendfsync always in Umgebungen mit langsamem Storage einzusetzen, ohne den Durchsatzverlust vorher gemessen zu haben. Auf Netzwerkspeicher mit hoher fsync-Latenz kann diese Einstellung den effektiven Schreibdurchsatz um eine Größenordnung reduzieren, was in Lasttests häufig unentdeckt bleibt, weil dort meist kleinere Datenmengen und geringere Parallelität als in Produktion getestet werden.
Ein zweiter Fehler ist, BGSAVE und BGREWRITEAOF gleichzeitig laufen zu lassen, ohne no-appendfsync-on-rewrite zu berücksichtigen. Beide Operationen forken den Prozess und erzeugen Copy-on-Write-Druck, wodurch der kombinierte Speicher-Overhead bei zeitgleicher Ausführung deutlich höher ausfällt als bei jeweils einzeln laufenden Operationen. Redis versucht standardmäßig, überlappende Rewrites zu vermeiden, aber bei manuell getriggerten Backups über BGSAVE parallel zu einem automatischen AOF-Rewrite kann es dennoch zu Überschneidungen kommen.
Ein dritter, subtilerer Fehler betrifft die Annahme, AOF-Persistence schütze automatisch vor Datenkorruption durch Anwendungsfehler. AOF protokolliert exakt die Kommandos, die die Anwendung sendet, inklusive fehlerhafter Lösch- oder Überschreiboperationen. Ein versehentliches FLUSHALL wird ebenso zuverlässig persistiert und beim nächsten Neustart erneut abgespielt wie jedes andere Kommando. AOF ersetzt daher kein separates Backup-Konzept mit Punkt-in-Zeit-Wiederherstellung.
Mironsoft
Redis-Betrieb, Persistence-Strategien und Backup-Infrastruktur
Die richtige fsync-Strategie für euren Redis-Betrieb?
Wir analysieren eure Schreiblast, messen den tatsächlichen Latenz-Impact von appendfsync-Optionen und konfigurieren AOF-Rewrite-Trigger, die zu Datenvolumen und Verfügbarkeitsanforderungen passen.
Durability-Analyse
appendfsync-Strategien gegen eure tatsächliche Verlusttoleranz abwägen
Rewrite-Tuning
auto-aof-rewrite-percentage und min-size an Datenvolumen anpassen
Incident-Readiness
redis-check-aof-Prozesse und Recovery-Runbooks etablieren
10. Zusammenfassung
AOF-Persistence protokolliert jede Schreiboperation als Kommando und erlaubt damit ein deutlich engeres Verlustfenster als reine RDB-Snapshots. Die Wahl der appendfsync-Strategie ist die zentrale Entscheidung: always für maximale Durability bei spürbarem Latenz-Overhead, everysec als empfohlener Kompromiss mit einer Sekunde potenziellem Verlust, no für maximale Performance bei größerem Risiko. Der AOF-Rewrite-Prozess hält die Dateigröße über BGREWRITEAOF unter Kontrolle, gesteuert durch auto-aof-rewrite-percentage und auto-aof-rewrite-min-size.
Das Multi-Part-Dateiformat seit Redis 7 mit Manifest-, Basis- und Inkrement-Dateien macht Rewrites robuster gegenüber Abstürzen als das alte Single-File-Format. redis-check-aof bleibt das zentrale Werkzeug zur Diagnose und Reparatur nach einem harten Absturz. Wichtig bleibt: AOF-Persistence schützt vor technischem Datenverlust, nicht vor logischen Anwendungsfehlern wie einem versehentlichen FLUSHALL, dafür braucht es weiterhin ein separates Backup-Konzept.
AOF-Persistence im Detail: Das Wichtigste auf einen Blick
fsync-Strategien
always für Zero-Loss, everysec als Standard, no nur bei klarer Performance-Priorität.
AOF-Rewrite
BGREWRITEAOF kompaktiert die Kommando-Historie, gesteuert über percentage- und min-size-Trigger.
Dateiformat
Manifest, Basis-Datei und Inkrement-Datei seit Redis 7, robuster gegen Absturz während Rewrite.
Reparatur
redis-check-aof --fix nach hartem Absturz, immer vorher eine Kopie der Originaldatei anlegen.