MAISON CODE .
/ Tech · Backend · Redis · Caching · Performance · Scale

Redis Caching: Die Architektur von Millisekunden

Warum Ihre Datenbank der Flaschenhals ist. Eine ausführliche technische Anleitung zu Redis-Mustern (Cache-Aside, Write-Through), Tag-Invalidierung und HyperLogLog.

AB
Alex B.
Redis Caching: Die Architektur von Millisekunden

In der Latenzhierarchie ist das Netzwerk langsam, die Festplatte träge, der RAM jedoch verzögerungsfrei.

  • PostgreSQL-Abfrage (SSD): 10 ms – 100 ms.
  • Redis-Abfrage (RAM): 0,2 ms.

Dieser 500-fache Geschwindigkeitsunterschied ist der einzige Grund, warum große Webanwendungen online bleiben. Wenn Amazon am Prime Day für jede Produktansicht auf seine SQL-Datenbank zugreifen würde, würde das gesamte Internet zusammenbrechen.

Bei Maison Code Paris behandeln wir Redis nicht nur als „Cache“, sondern als primären Datenstrukturserver. Es ist die taktische Ebene, die die strategische Ebene (die Datenbank) schützt.

Warum Maison Code darüber spricht

Bei Maison Code Paris fungieren wir als das architektonische Gewissen unserer Kunden. Wir übernehmen oft „moderne“ Stacks, die ohne grundlegendes Verständnis für Skalierung gebaut wurden.

Wir diskutieren dieses Thema, weil es einen kritischen Wendepunkt in der technischen Reife darstellt. Die korrekte Implementierung unterscheidet ein fragiles MVP von einer widerstandsfähigen Plattform auf Unternehmensniveau.

Die Muster: So zwischenspeichern Sie richtig

Caching ist einfach. Der Cache kann nicht ungültig gemacht werden. Es gibt zwei Hauptimplementierungsmuster, die wir verwenden.

1. Cache-Aside (Lazy Loading)

Dies ist die Standardeinstellung. Für das Lesen/Schreiben ist die Anwendung zuständig.

„Typoskript // db/repo.ts import { redis } from ’./redis’;

asynchrone Funktion getProduct(id: string) { const key = product:€{id};

// 1. Cache prüfen const zwischengespeichert = Warten auf redis.get(key); if (cached) return JSON.parse(cached);

// 2. Cache-Miss -> DB prüfen const data = waiting db.product.findUnique({ id });

// 3. Cache füllen if (Daten) { // Cache für 60 Sekunden (TTL) wait redis.set(key, JSON.stringify(data), ‘EX’, 60); }

Rückgabedaten; } „

  • Vorteile: Belastbar. Wenn Redis stirbt, funktioniert die App (langsam).
  • Nachteile: Fenster „Veraltete Daten“. Der Benutzer sieht möglicherweise den alten Preis für 60er Jahre.

2. Durchschreiben (ereignisgesteuerte Invalidierung)

Wir bevorzugen dies für hochkonsistente Daten (wie Inventar). Wir hören auf Datenbankänderungen (CDC oder Anwendungsereignisse) und zerstören den Cache.

„Typoskript //services/product.ts asynchrone Funktion updatePrice(id: string, price: number) { // 1. DB aktualisieren (Quelle der Wahrheit) wait db.product.update({ where: { id }, data: { price } });

// 2. Cache sofort ungültig machen Warten Sie auf redis.del(product:€{id}); } „

Das schwierigste Problem: Invalidierung durch Beziehung (Tags)

Stellen Sie sich vor, Sie zwischenspeichern:

  1. „Produkt:123“.
  2. „Kategorie:Schuhe“ (Enthält Produkt 123)

Wenn Sie Produkt 123 aktualisieren, löschen Sie „Produkt:123“. Aber „category:shoes“ enthält immer noch die alte Version des Produkts! Sie müssen auch wissen, welche Kategorien den Schlüssel 123 enthalten.

Wir implementieren Tag-basierte Invalidierung mithilfe von Redis-Sets.

  1. Beim Erstellen von „category:shoes“ verfolgen wir Abhängigkeiten: „SADD-Tags:Produkt:123 Kategorie:Schuhe“.
  2. Bei der Aktualisierung von Produkt 123:
    • Finden Sie alle davon abhängigen Schlüssel: SMEMBERS tags:product:123 -> Gibt ['category:shoes'] zurück.
    • Löschen Sie sie: „DEL Kategorie: Schuhe“.
    • Bereinigen Sie den Tag-Satz.

Diese Logik ist komplex, löst aber das Problem „Warum wird auf der Homepage immer noch das alte Bild angezeigt?“ Fehler für immer.

Serialisierung: Die versteckten CPU-Kosten

Das Speichern von JSON-Strings („JSON.stringify“) ist Standard, aber ineffizient.

  1. Speicher: JSON ist ausführlich. „aktiv“: true benötigt 15 Bytes.
  2. CPU: „JSON.parse“ blockiert die Ereignisschleife. Bei einer Nutzlast von 1 MB (große Homepage) benötigt das Parsen 20 ms CPU-Zeit.

Lösung: MessagePack (msgpack). Es handelt sich um ein binäres Serialisierungsformat. Es ist schneller und kleiner.

„Typoskript import { pack, unpack } from ‘msgpackr’; Importieren Sie Redis aus „ioredis“;

const redis = new Redis({ // ioredis unterstützt binäre Puffer keyPrefix: ‘v1:’, });

Warten Sie auf redis.set(‘key’, pack(largeObject)); // Speichert Puffer const buffer = waiting redis.getBuffer(‘key’); const obj = unpack(buffer); „

Benchmarks zeigen, dass msgpack bei großen Strukturen dreimal schneller zu kodieren/dekodieren ist als natives JSON.

Erweiterte Strukturen: Nicht nur Schlüssel und Werte

Redis ist ein „Data Structure Server“. Hören Sie auf, es wie eine „Map<String, String>“ zu verwenden.

1. HyperLogLog (ungefähre Zählung)

Problem: „Zählen Sie heute einzelne Besucher.“ Naiv: Speichern Sie jede IP in einem Set. „SADD {ip}“. Für 100 Millionen Benutzer sind hierfür 1,5 GB RAM erforderlich. Redis-Lösung: „PFADD Visitors {ip}“. HyperLogLog verwendet probabilistische Mathematik (Hashes). Es zählt 100 Millionen einzigartige Elemente mit 12 KB RAM. Die Zuverlässigkeit beträgt 99,19 %. Sie benötigen genaue Besucherzahlen? Nein. Sie müssen wissen: „Ist es 10.000 oder 100.000?“. HLL ist perfekt.

2. Georäumliche Indizes

Problem: „Finden Sie Geschäfte in meiner Nähe.“ Naiv: SQL-Haversine-Formel (Langsam). Redis-Lösung: „GEOADD speichert 2.3522 48.8566 „Paris““. „GEORADIUS speichert 2,35 48,85 10 km“ gibt mithilfe der Geohash-Sortierung Ergebnisse in Mikrosekunden zurück.

3. Ratenbegrenzung (Der Token-Bucket)

Wir schützen unsere APIs vor DDoS mithilfe von Redis-Zählern. Wir verwenden ein Lua-Skript (um die Atomizität sicherzustellen), das den „Sliding Window“-Algorithmus implementiert. INCR-Schlüssel. „EXPIRE-Taste 60“. Wenn „> 100“, Ablehnen.

Das Problem der donnernden Herde

Wenn eine Cache-Funktionalität lebenswichtig ist, ist ihr Ausfall katastrophal. Szenario:

  1. Der Schlüssel „home_page“ läuft um 12:00:00 ab.
  2. Um 12:00:01 Uhr fordern 5.000 Benutzer die Homepage an.
  3. 5.000 Anfragen erhalten ein „Cache Miss“.
  4. 5.000 Anfragen gehen gleichzeitig in die Postgres-Datenbank ein.
  5. Der Datenbank-Ausgabepuffer ist voll. Datenbank stürzt ab.
  6. Die Website fällt aus.

Lösung: Cache-Sperre (The Mutex). Wenn ein „Miss“ auftritt, versucht der Code, eine Sperre („SETNX lock:home_page“) zu erhalten.

  • Gewinner: Erstellt die Seite. Schreibt in den Cache. Gibt die Sperre frei.
  • Verlierer: 100 ms warten. Überprüfen Sie den Cache erneut. Ergebnis: Nur 1 Abfrage trifft auf die Datenbank.

10. Redis Cluster vs. Sentinel (Hochverfügbarkeit)

Single Node Redis ist ein Single Point of Failure. Sentinel: Überwacht den Meister. Failover auf Replikat. Cluster: Shards von Daten über mehrere Knoten hinweg. Für E-Commerce benötigen wir selten Cluster (Sharding ist komplex). Eine Redis-Instanz kann 100.000 Operationen/Sek. verarbeiten. Wir bevorzugen Sentinel. Aber Vorsicht: Die Client-Konfiguration für Sentinel ist schwierig. redis = new Redis({ sentinels: [{ host: 'sentinel-1', port: 26379 }], name: 'mymaster' }). Wenn Sie die Master-IP fest codieren, funktioniert das Failover nicht.

11. Lua Scripting: Atomarität ist König

Sie möchten „Guthaben prüfen, Geld abziehen“. Dies in Node.js (Get -> Logic -> Set) zu tun, ist eine Race Condition. Wir schreiben Lua-Skripte, die in Redis ausgeführt werden. Lua-Skripte sind atomar. Während der Ausführung des Skripts wird kein anderer Befehl ausgeführt. Dies garantiert, dass der „Inventarabzug“ auch bei 500 gleichzeitigen Käufern perfekt konsistent ist.

13. Intelligente Strategien zur Cache-Erwärmung

Darauf zu warten, dass der erste Benutzer ein „Miss“ trifft und die Latenzstrafe zahlt, ist eine schlechte UX. Wir implementieren Active Cache Warming.

  1. Bei der Bereitstellung: Löst ein Skript „warm-cache.ts“ aus.
  2. Logik: Ruft die Top 500 URLs von Google Analytics ab (über API).
  3. Aktion: Ruft die interne API „localhost:3000/api/render?url={top_url}“ auf. Dadurch wird der Server gezwungen, die Seite zu generieren und Redis zu füllen, bevor Datenverkehr auftritt. Das Ergebnis ist eine Latenz von 0 ms für 80 % der Benutzer unmittelbar nach einer Bereitstellung.

14. Über den Schlüsselwert hinaus: Redis Stack (Suche und JSON)

Mit dem neuen Redis Stack können Sie SQL-ähnliche Abfragen für JSON-Dokumente ausführen. FT.SEARCH productIdx „@title:Shoes @price:[0 100]“ Dies läuft im Speicher. Es ist 50-mal schneller als Elasticsearch für kleine Datensätze (<1 Mio. Elemente). Wir verwenden dies für „Instant Search“-Funktionen, bei denen Algolia zu teuer oder zu langsam ist.

15. Fazit

Redis ist das leistungsstärkste Tool im Utility-Gürtel des Backend-Ingenieurs. Es schließt die Lücke zwischen der starren, langsamen Welt der ACID-Datenbanken und dem chaotischen, schnellen Anforderungsvolumen des modernen Webs.

Aber es erfordert Disziplin. Ein ungezähmter Cache (ohne TTLs, ohne Räumungsrichtlinie und mit riesigen Schlüsseln) ist ein Speicherleck, das nur darauf wartet, Ihren Server zum Absturz zu bringen.


**[Beauftragen Sie unsere Architekten](/contact)**.