Redis als Cache vs. Redis als Datenbank
Redis taucht meist leise in einem Projekt auf. Jemand setzt es ein, um eine langsame Datenbankabfrage zu cachen, konfiguriert es mit den Standardwerten und macht weiter. Achtzehn Monate später hält dieselbe Instanz auch Benutzer-Sessions, Rate-Limiting-Zähler, Feature-Flag-Status und ein oder zwei Queues, von denen nichts jemals irgendwo anders geschrieben wurde. Die Instanz hat sich nicht verändert. Was ihr anvertraut wird, schon, und die Konfiguration hat das nie nachgeholt.
Cache-Modus: Datenverlust ist okay, mit Absicht
Wenn Redis wirklich nur ein Cache ist, ist jeder Key darin eine Kopie von etwas, das anderswo existiert, oder die Kosten, es neu zu berechnen. Startet Redis neu und der Cache ist leer, ist die Anwendung kurz langsamer, während sie sich neu befüllt, aber nichts geht verloren. Für diesen Fall sollte die Konfiguration genau das aktiv nutzen:
maxmemory 2gb
maxmemory-policy allkeys-lru
save ""
maxmemory-policy allkeys-lru bedeutet, dass Redis beim Erreichen des Speicherlimits die am längsten nicht genutzten Keys verwirft, um Platz für neue zu schaffen, genau das richtige Verhalten für einen Cache. save "" deaktiviert RDB-Snapshots komplett, hier gibt es nichts, das die Disk-I/O wert wäre, da sich alles rekonstruieren lässt.
Datenbank-Modus: Datenverlust ist ein echter Incident
Sobald Redis etwas hält, für das es keine andere Quelle gibt, eine Session, deren Verlust jeden Nutzer ausloggt, eine Queue mit noch unverarbeiteten Jobs, ein Zähler, der unerwartet zurückspringt, kehrt sich die Rechnung komplett um. Jetzt ist allkeys-lru aktiv gefährlich: Redis kann sich entscheiden, ein Session-Token zu verwerfen, um Platz für einen Cache-Eintrag zu schaffen, und das ist kein Performance-Hickup, das ist Datenverlust. Die Konfiguration muss das widerspiegeln:
maxmemory-policy noeviction
appendonly yes
appendfsync everysec
noeviction bedeutet, dass Redis bei vollem Speicher einen Fehler auf Schreibvorgänge zurückgibt, statt still etwas zu löschen, was dazu zwingt, ein Kapazitätsproblem zu bemerken und zu beheben, statt unbemerkt Daten zu verlieren. appendonly yes aktiviert die Append-Only-File-Persistenz, und everysec balanciert Dauerhaftigkeit gegen Schreibperformance, im Crash-Fall geht höchstens eine Sekunde an Schreibvorgängen verloren, statt allem seit dem letzten RDB-Snapshot.
Die eigentliche Gefahr ist die unbeabsichtigte Mischung
Das riskante Setup ist nicht "Cache" oder "Datenbank", es ist beides, in derselben Instanz, unter einer Konfiguration, die für eines von beiden gewählt wurde. allkeys-lru mit Cache-Einträgen und Session-Tokens im selben Keyspace bedeutet, dass Redis nicht unterscheiden kann zwischen einem Cache-Eintrag, dessen Verlust egal ist, und einer Session, bei der das nicht so ist, es verwirft, was am längsten nicht genutzt wurde, und das kann beides sein. Ich habe erlebt, wie das einen sporadischen "Nutzer werden unter Last zufällig ausgeloggt"-Bug verursacht hat, der eine Weile gedauert hat, bis er auf Cache-Druck zurückgeführt wurde, der Session-Keys verdrängt hat, weil nichts am Symptom auf Redis' Eviction-Policy hindeutete.
Trennung muss nicht separate Server bedeuten
Redis unterstützt mehrere logische Datenbanken in einer Instanz (SELECT 0 bis SELECT 15 standardmäßig), aber diese teilen sich maxmemory und die Eviction-Policy, lösen das Problem also nicht wirklich. Was es löst, ist, zwei Redis-Instanzen zu betreiben, auch auf demselben Host, jede mit einer zur Aufgabe passenden Konfiguration:
redis-server /etc/redis/cache.conf --port 6379
redis-server /etc/redis/store.conf --port 6380
Die Cache-Instanz bekommt allkeys-lru und keine Persistenz. Die Store-Instanz bekommt noeviction und AOF-Persistenz, und sollte idealerweise gesichert werden wie jede andere Datenbank, denn an diesem Punkt ist sie eine.
Die Frage, die sich von Zeit zu Zeit lohnt
Für jede Redis-Instanz, die schon eine Weile läuft: Wenn sie jetzt sofort verschwinden würde, würde dann etwas dauerhaft verloren gehen, nicht nur langsamer werden? Wenn die Antwort nein ist, ist die Cache-Konfiguration richtig, und es gibt nichts zu ändern. Wenn die Antwort ja ist, auch nur für ein einziges Key-Pattern, ist das jetzt eine Datenbank, egal ob das jemals bewusst entschieden wurde, und es lohnt sich, sie auch so zu konfigurieren, zu überwachen und zu sichern, bevor diese Frage auf die harte Art beantwortet wird.