Werbung
Redis hat die Tendenz, sich unglaublich leise in ein Projekt einzuschleichen. Irgendjemand wirft es ein, um eine schmerzhaft langsame Datenbankabfrage zu cachen, belässt es bei den absoluten Standardeinstellungen und geht zur Tagesordnung über.
Achtzehn Monate später speichert exakt dieselbe Instanz aktive User-Sessions, Rate-Limiting-Counter, Feature-Flag-Status und vielleicht noch eine Background-Job-Queue. Absolut nichts davon wurde jemals zuerst in eine permanente Datenbank geschrieben. Die Redis-Instanz selbst hat sich nicht verändert. Das, was man ihr blind anvertraut, hat sich jedoch massiv verändert – und die Konfiguration ist so gut wie nie mitgezogen.
Cache-Modus: Datenverlust ist hier By Design völlig in Ordnung
Wenn Redis tatsächlich und ausschließlich als Cache fungiert, ist jeder einzelne Key darin eine temporäre Kopie von etwas, das anderswo bereits existiert, oder lediglich die eingesparten Rechenkosten, um es neu zu generieren.
Wenn Redis komplett neu startet und der Cache blank geputzt wird, ist die Applikation zwar spürbar langsamer, bis er wieder gefüllt ist, aber es ist absolut nichts permanent verloren. Für diesen spezifischen Fall greift die korrekte Konfiguration genau diese Realität aktiv auf:
maxmemory 2gb
maxmemory-policy allkeys-lru
save ""
maxmemory-policy allkeys-lru bedeutet: In der Sekunde, in der Redis an sein hartes Speicherlimit stößt, wirft es aggressiv diejenigen Keys raus, die am längsten nicht mehr benutzt wurden (Least Recently Used), um Platz für neue zu schaffen. Das ist das absolut korrekte Verhalten für einen Cache. save "" deaktiviert das RDB-Snapshotting vollständig. Hierin liegt absolut nichts, wofür sich die Festplatten-I/O einer Persistierung lohnen würde, da alles zu 100 % rekonstruierbar ist.
Datenbank-Modus: Datenverlust ist hier ein echter Incident
In exakt dem Moment, in dem Redis Dinge vorhält, für die es keine andere Source of Truth gibt – eine User-Session, deren Verlust einen Kunden rigoros ausloggt, eine Queue noch unbearbeiteter Background-Jobs oder ein kritischer Counter, der sich plötzlich unerwartet auf null setzt –, dreht sich das gesamte Kalkül um 180 Grad.
Jetzt ist allkeys-lru aktiv gefährlich. Redis kann und wird sich jederzeit entscheiden, den aktiven Session-Token eines Users völlig geräuschlos zu löschen, nur um Platz für einen zufälligen Cache-Eintrag zu machen. Das ist kein Performance-Huckler mehr; das ist direkter Datenverlust. Die Konfiguration muss dieser neuen Realität unbedingt angepasst werden:
maxmemory-policy noeviction
appendonly yes
appendfsync everysec
noeviction bedeutet, dass Redis Schreibvorgänge mit einem harten Error quittiert, sobald der Speicher komplett voll ist, anstatt klammheimlich etwas zu löschen. Das zwingt Sie brutal dazu, ein Kapazitätsproblem auch tatsächlich zu bemerken und zu lösen, anstatt still und leise kritische Daten zu vernichten. appendonly yes aktiviert das Append-Only File (AOF) für eine felsenfeste Persistierung. Die Einstellung everysec balanciert dabei Dauerhaftigkeit und Schreibperformance perfekt aus. Bei einem harten Absturz verlieren Sie höchstens eine einzige Sekunde an Schreibvorgängen und nicht alles seit dem letzten RDB-Snapshot.
Die eigentliche Gefahr: Unbeabsichtigtes Mischen
Das riskanteste Setup ist weder "reiner Cache" noch "reine Datenbank". Es ist, beides auf exakt derselben Instanz laufen zu lassen, unter einer Konfiguration, die ausschließlich für einen der beiden Use-Cases gewählt wurde.
Wer allkeys-lru mit wegwerfbaren Cache-Einträgen und hochkritischen Session-Tokens im exakt selben Keyspace betreibt, zwingt Redis in eine Situation, in der es buchstäblich nicht mehr unterscheiden kann zwischen einem Cache-Eintrag, der problemlos gelöscht werden darf, und einer Session, bei der das ein Desaster ist. Es wirft einfach raus, was am längsten nicht benutzt wurde – was beides sein kann. Ich habe persönlich erlebt, wie das einen extrem sporadischen "User fliegen unter Last zufällig raus"-Bug verursacht hat, dessen Suche Tage dauerte. Der Grund: Einfach nichts an diesem Symptom deutete direkt auf die Eviction-Policy von Redis hin.
Trennung bedeutet nicht zwingend separate Server
Redis unterstützt absolut logische Datenbanken auf einer einzigen Instanz (standardmäßig SELECT 0 bis SELECT 15). Das Problem ist nur: Diese teilen sich exakt dasselbe maxmemory-Limit und dieselbe Eviction-Policy. Sie lösen dieses spezifische Problem also überhaupt nicht.
Was tatsächlich funktioniert, ist, zwei völlig separate Redis-Instanzen laufen zu lassen – selbst wenn es auf exakt demselben Host ist –, wobei jede eine Konfiguration erhält, die perfekt auf ihren Job abgestimmt ist:
redis-server /etc/redis/cache.conf --port 6379
redis-server /etc/redis/store.conf --port 6380
Die Cache-Instanz bekommt allkeys-lru und exakt null Persistierung. Die Store-Instanz bekommt noeviction und AOF-Persistierung. Im Idealfall wird sie obendrein genauso gesichert wie jede andere Datenbank auch, denn zu diesem Zeitpunkt ist sie rechtlich gesehen absolut eine.
Die Frage, die man sich regelmäßig stellen sollte
Bei jeder Redis-Instanz, die schon eine Weile läuft, sollte man sich Folgendes fragen: Wenn dieses Ding auf der Stelle explodieren würde, wäre dann irgendetwas permanent verloren, anstatt dass die Anwendung nur kurzzeitig langsam wird?
Wenn die Antwort ein hartes Nein ist, ist die Cache-Konfiguration korrekt und es gibt nichts zu tun. Wenn die Antwort jedoch Ja ist – und sei es nur für ein einziges Key-Pattern –, dann ist das jetzt offiziell eine Datenbank. Völlig unabhängig davon, ob das jemals jemand absichtlich so entschieden hat, ist es absolut geboten, das Ding genau wie eine Datenbank zu konfigurieren, zu überwachen und zu sichern – bevor man die Antwort auf diese Frage auf die harte Tour bekommt.
Werbung