Was ist Caching?
Caching bedeutet, Daten vorübergehend zu speichern, die die Anwendung wahrscheinlich erneut benötigt.
Stellen wir uns eine E-Commerce-Website vor, auf der Tausende von Nutzern immer wieder dieselbe Produktseite öffnen. Die Anwendung könnte jedes Mal eine Abfrage an die Datenbank senden, wenn jemand diese Seite aufruft. Wenn sich die Produktinformationen jedoch nicht geändert haben, gibt es wenig Grund, dieselbe Datenbankabfrage immer wieder auszuführen.
Stattdessen kann die Anwendung die Daten einmal aus der Datenbank abrufen, sie in einem Cache wie Redis speichern und nachfolgende Anfragen direkt aus dem Cache bedienen.
Der grundlegende Ablauf sieht so aus:
Erste Anfrage: Anwendung → Datenbank → Redis → Nutzer
Weitere Anfragen: Anwendung → Redis → Nutzer
Das Ergebnis können weniger Datenbankabfragen, eine geringere Auslastung und schnellere Antwortzeiten sein.
Warum die Datenbank nicht immer der Flaschenhals ist
Wenn eine Anwendung langsamer wird, gehört die Datenbank oft zu den ersten Dingen, die Entwickler untersuchen. Manchmal liegt das Problem tatsächlich dort. Aber nicht immer.
Eine Datenbankabfrage kann optimal umgesetzt sein und trotzdem teuer werden, wenn sie tausende Male ausgeführt wird.
Stellen wir uns beispielsweise ein Dashboard vor, das Statistiken auf Basis einer großen Menge historischer Daten berechnet. Die Abfrage selbst benötigt vielleicht nur 200 Millisekunden. Das klingt zunächst nicht nach viel. Wenn jedoch Hunderte Nutzer dieselbe Berechnung jede Minute anfordern, führt die Datenbank immer wieder dieselbe Arbeit aus.
Caching kann hier helfen, indem das Ergebnis einmal berechnet und für nachfolgende Anfragen wiederverwendet wird.
Das bedeutet nicht, dass Caching automatisch hinzugefügt werden sollte, sobald eine Anwendung langsamer wird. Bevor eine weitere Ebene in die Architektur eingeführt wird, sollte zunächst klar sein, wo sich der tatsächliche Flaschenhals befindet.
Manchmal ist die richtige Lösung ein Datenbankindex. Manchmal eine bessere Abfrage. Und manchmal muss die Logik der Anwendung selbst optimiert werden.
Redis vs. Datenbank
Redis und eine relationale Datenbank wie PostgreSQL lösen unterschiedliche Probleme.
PostgreSQL ist normalerweise die Single Source of Truth. Es ist dafür ausgelegt, dauerhafte Daten zu speichern, Transaktionen zu verarbeiten, Beziehungen zwischen Entitäten zu verwalten und komplexe Abfragen auszuführen.
Redis ist ein In-Memory-Datenspeicher. Seine Stärke liegt in der Geschwindigkeit. Dadurch eignet es sich besonders für Daten, auf die häufig zugegriffen wird, die temporär sind oder deren wiederholtes Abrufen aufwendig wäre.
Typische Einsatzbereiche für Redis sind:
- Ergebnisse aufwendiger Datenbankabfragen
- Benutzersitzungen
- temporäre Tokens
- Rate Limiting
- Zähler
- API-Antworten
- häufig abgerufene Daten, die sich relativ selten ändern
Eine typische Architektur sieht daher so aus:
Anwendung → Redis → PostgreSQL
PostgreSQL bleibt die zuverlässige Datenquelle, während Redis als schnelle Ebene zwischen Anwendung und Datenbank fungiert.
Das Cache-Aside-Pattern
Eine der am häufigsten verwendeten Caching-Strategien ist das sogenannte Cache-Aside-Pattern.
Die Idee ist einfach: Die Anwendung prüft zunächst, ob die benötigten Daten bereits im Cache vorhanden sind.
Wenn dies der Fall ist, kann die Anwendung sie sofort verwenden.
Sind die Daten nicht vorhanden, ruft die Anwendung sie aus der Datenbank ab, speichert sie in Redis und gibt sie anschließend an den Nutzer zurück.
Der Ablauf sieht so aus:
- Die Anwendung erhält eine Anfrage.
- Sie prüft Redis auf die angeforderten Daten.
- Wenn die Daten vorhanden sind, gibt Redis sie zurück.
- Wenn sie nicht vorhanden sind, fragt die Anwendung PostgreSQL ab.
- Das Ergebnis wird in Redis gespeichert.
- Die Anwendung gibt das Ergebnis an den Nutzer zurück.
Bei der nächsten Anfrage können die Daten dann direkt aus Redis geliefert werden.
Dieser Ansatz eignet sich besonders gut, wenn dieselben Daten häufig angefordert werden, sich aber nicht sehr oft ändern.
TTL: Wie lange sollten Daten im Cache bleiben?
Gecachte Daten sollten nicht unbedingt für immer im Cache bleiben.
Hier kommt TTL (Time To Live) ins Spiel. TTL definiert, wie lange ein gespeicherter Wert gültig bleibt, bevor er automatisch abläuft.
Beispielsweise könnte ein Produkt in Redis mit einer TTL von fünf Minuten gespeichert werden. Nach Ablauf dieser fünf Minuten verfällt der gecachte Wert und die Anwendung kann aktuelle Daten aus der Datenbank abrufen.
Die richtige TTL hängt von der Art der Daten ab.
Bei Daten, die sich häufig ändern, kann eine kurze TTL sinnvoll sein. Bei Daten, die sich nur selten ändern, kann der Cache deutlich länger gültig bleiben.
Die entscheidende Frage lautet daher nicht nur:
„Wie lange können wir diese Daten cachen?“
Sondern vielmehr:
„Wie lange können diese Daten gefahrlos leicht veraltet sein?“

Der schwierigste Teil? Cache-Invalidierung
Es gibt einen bekannten Spruch aus der Softwareentwicklung:
„There are only two hard things in Computer Science: cache invalidation and naming things.“
Und tatsächlich kann die Cache-Invalidierung kompliziert werden.
Stellen wir uns vor, dass der Preis eines Produkts in Redis gespeichert ist. Der Preis wird in PostgreSQL geändert, aber der alte Wert befindet sich weiterhin im Cache. Nutzer könnten dadurch weiterhin den veralteten Preis sehen, bis der Cache abläuft.
Es gibt verschiedene Möglichkeiten, damit umzugehen.
Eine Möglichkeit besteht darin, den Cache-Eintrag jedes Mal zu löschen, wenn sich die zugrunde liegenden Daten ändern.
Eine andere besteht darin, den gecachten Wert gleichzeitig mit dem Datenbankwert zu aktualisieren.
Eine dritte Möglichkeit ist, sich auf eine TTL zu verlassen und zu akzeptieren, dass der gecachte Wert für einen begrenzten Zeitraum leicht veraltet sein kann.
Welche Strategie sinnvoll ist, hängt davon ab, wie wichtig die Aktualität der Daten ist. Eine Verzögerung von einigen Sekunden kann bei einer Dashboard-Statistik völlig akzeptabel sein, bei einem Zahlungsbetrag jedoch offensichtlich nicht.
Was kann schiefgehen?
Caching verbessert die Performance, bringt aber auch eine zusätzliche Ebene der Komplexität mit sich.
Ein häufiges Problem sind veraltete Daten. Der Cache kann eine ältere Version der Informationen enthalten als die Datenbank.
Ein weiteres Problem ist ein Cache Stampede. Wenn ein häufig verwendeter Cache-Eintrag abläuft und gleichzeitig sehr viele Anfragen eintreffen, können alle versuchen, dieselben Daten gleichzeitig aus der Datenbank abzurufen.
Auch der Speicher muss berücksichtigt werden. Redis speichert Daten im Arbeitsspeicher, und dieser ist nicht unbegrenzt verfügbar. Alles zu cachen ist daher selten eine gute Strategie.
Schließlich erhöht Caching auch die operative Komplexität. Es gibt einen weiteren Dienst, der überwacht werden muss, einen weiteren möglichen Fehlerpunkt und einen zusätzlichen Teil der Architektur, den Entwickler verstehen und verwalten müssen.
Wann sollte man Redis nicht verwenden?
Redis ist nicht automatisch die Antwort auf jedes Performance-Problem.
Wenn eine Anwendung klein ist und die Datenbank die aktuelle Last problemlos bewältigt, kann Redis einfach unnötige Komplexität hinzufügen.
Wenn eine Abfrage beispielsweise deshalb langsam ist, weil ein Index fehlt oder die Abfrage schlecht geschrieben ist, kann das Caching des Ergebnisses das eigentliche Problem lediglich verstecken.
Caching kann auch ungeeignet sein, wenn Daten jederzeit vollständig aktuell sein müssen oder sich so häufig ändern, dass gecachte Werte kaum einen Nutzen haben.
Der erste Schritt sollte daher immer eine Messung sein.
Finde zunächst den tatsächlichen Flaschenhals. Entscheide anschließend, ob Caching dieses Problem tatsächlich lösen kann.
Redis + Django + PostgreSQL
Redis lässt sich sehr gut in Anwendungen integrieren, die mit Django und PostgreSQL entwickelt wurden.
Django bietet bereits ein eigenes Caching-Framework, das mit Redis verbunden werden kann. Eine typische Architektur könnte so aussehen:
Django → Redis → PostgreSQL
Die Anwendung prüft zunächst Redis auf häufig angeforderte Daten. Sind die Daten dort nicht verfügbar, fragt Django PostgreSQL ab, speichert das Ergebnis in Redis und liefert es an den Nutzer aus.
Das kann besonders für Anwendungen nützlich sein, bei denen deutlich mehr Lese- als Schreibvorgänge stattfinden.
Wenn beispielsweise Tausende Nutzer immer wieder dieselben, relativ stabilen Informationen anfordern, kann Caching verhindern, dass PostgreSQL dieselbe Arbeit immer wieder ausführen muss.
Cache ist kein Ersatz für eine gute Architektur
Redis ist kein magischer Performance-Schalter.
Bevor Caching eingeführt wird, sollte klar sein, was die Anwendung tatsächlich verlangsamt. Die Lösung könnte ein besserer Datenbankindex, eine effizientere Abfrage, eine optimierte Anwendungslogik oder ein anderer architektonischer Ansatz sein.
Caching ist besonders sinnvoll, wenn Daten aufwendig abzurufen oder zu berechnen sind und anschließend wiederholt angefordert werden.
Die wichtigste Frage lautet daher:
Welche Daten lohnt es sich, einmal zu laden und anschließend mehrfach zu verwenden?
Genau hier kann Redis einen echten Unterschied machen – nicht indem es die Datenbank ersetzt, sondern indem es reduziert, wie oft die Anwendung die Datenbank dieselbe Arbeit erledigen lässt.