Redis jenseits des Caching: Sessions, Rate Limits, Warteschlangen und Pub/Sub in Node.js
Sieben Redis-Muster für Node.js-Backends – Caching mit TTLs, OTP-Schlüssel, Geschwindigkeitsbeschränkungen, BullMQ-Aufgaben, Sessions, Pub/Sub-Beschränkungen sowie wann Redis nicht verwendet werden sollte.
Frühe mentale Modelle betrachten Redis als „nur einen Cache“: Ein Wert wird gespeichert, eine Ablaufzeit festgelegt, er wird schneller gelesen als die Datenbank – und das war’s.
Dieses Bild ist nicht falsch. Es ist jedoch unvollständig.
In der Praxis wird Redis für schnelle API-Antworten, gemeinsame Sessions, Missbrauchszähler, einmalige Codes, verzögerte Job-Queues, leichte Echtzeit-Versandmöglichkeiten sowie andere kurzlebige Werte eingesetzt.
Eine bessere Sichtweise: Betrachten Sie Redis als einen schnellen In-Memory-Datenspeicher mit vielen Backend-Funktionen – Caching ist nur die erste davon.
1. Redis kann eine API erheblich beschleunigen
Fangen Sie mit Caching an.
Betrachten Sie GET /products.
Ohne Cache könnte jede Anfrage so aussehen:
Client → Node.js API → PostgreSQL → Node.js API → Client
Eine teure Abfrage bei Tausenden Aufrufen bedeutet, dass die Datenbank dieselbe Arbeit immer wieder ausführt.
Redis kann sich vor diese Abfrage setzen.
Kunde → Node.js API → Redis
Bei einem Erfolg wird sofort zurückgegeben. Bei einem Misserfolg wird PostgreSQL abgefragt, das Ergebnis in Redis gespeichert und anschließend zurückgegeben.
Ein einfaches ioredis-Beispiel:
import Redis from "ioredis";
const redis = new Redis(process.env.REDIS_URL);
async function getProducts() {
const cached = await redis.get("products");
if (cached) {
return JSON.parse(cached);
}
const products = await database.product.findMany();
await redis.set(
"products",
JSON.stringify(products),
"EX",
300
);
return products;
}h
Hier bedeutet EX, dass der in Cache gespeicherte Inhalt nach 300 Sekunden abläuft.
Es gibt weiterhin ein schwerwiegendes Problem: die Cache-Invalidierung.
Nehmen wir an, Redis enthält product:123 → price:500, während PostgreSQL nun 600 speichert. Die Datenbank ist korrekt; Redis könnte dennoch 500 zurückgeben.
Caching bedeutet nicht einfach, „alles in Redis zu speichern“. Teams müssen weiterhin folgende Aspekte berücksichtigen:
- TTLs
- Invalidierung
- Veraltete Daten
- Misserfolge bei der Abfrage
- Cache-Fehler
Einen Wert in Redis zu speichern ist einfach. Ihn korrekt zu halten, ist schwieriger.
2. Redis eignet sich gut für temporäre Daten
Kurzlebige Werte eignen sich hervorragend: einmalige Anmeldecodes, Reset-Links, Verifizierungstoken, vorübergehende Sitzungsdaten, Missbrauchszähler sowie Warnschlösser.
Beispiel: Erstellung eines einmaligen Codes:
const otp = "482913";
await redis.set(
`otp:${userId}`,
otp,
"EX",
300
);
Der OTP läuft automatisch nach fünf Minuten ab.
Es ist weder eine spezielle OTP-Tabelle noch ein separater Aufräumvorgang erforderlich.
Auslesen mit:
const otp = await redis.get(`otp:${userId}`);
Nach Ablauf der TTL löscht Redis den Schlüssel gemäß seinem Ablaufmechanismus.
Dieses Muster macht Redis zu einem idealen Speicher für kurzlebige Anwendungsdaten.
3. Redis kann bei der Rate-Limiting-Hilfe sein
Nehmen wir POST /login.
Ohne Beschränkungen kann ein Client Anmeldeprobe überschwemmen – tausende Anfragen in Folge.
Redis kann einen Zähler für den Client speichern:
const key = `login-attempts:${ip}`;
const attempts = await redis.incr(key);
if (attempts === 1) {
await redis.expire(key, 60);
}
if (attempts > 10) {
throw new Error("Too many requests");
}
Die Struktur lautet: IP → Redis-Zähler → Anzahl → Limit.
Dies ist umso wichtiger, wenn mehrere API-Server hinter einem Load Balancer stehen. Prozessinterne In-Memory-Zähler sind nicht global. Redis bietet diesen Servern einen gemeinsamen Speicher.
4. Redis kann Hintergrundaufgaben ausführen
Bei der Erstellung eines Kontos müssen möglicherweise ein Benutzer erstellt, eine Willkommens-E-Mail gesendet, Daten generiert, ein anderer Dienst benachrichtigt und weitere Aufgaben durchgeführt werden.
Die HTTP-Anfrage sollte nicht auf all das warten.
Legen Sie die Aufgaben stattdessen in eine Warteschlange:
Client → API → Warteschlange → Antwort
Dann:
Warteschlange → Worker → Aufgabe verarbeiten
BullMQ ist eine gängige Node.js-Lösung, die Redis verwendet:
await emailQueue.add("welcome-email", {
userId: user.id,
email: user.email,
});
Ein Worker verarbeitet die Aufgabe separat:
const worker = new Worker(
"email",
async (job) => {
if (job.name === "welcome-email") {
await sendWelcomeEmail(job.data.email);
}
},
{
connection: redisConnection,
}
);
Die API muss nicht auf die Antwort des E-Mail-Anbieters warten, bevor sie dem Benutzer antwortet.
Das eignet sich für Aufgaben, die langsam sind, wiederholt ausgeführt werden können, von externen Diensten abhängig sind, viel CPU-Ressourcen benötigen oder vor der Antwort der API nicht erforderlich sind.
Geteilte Verantwortlichkeiten: die API kümmert sich um die Anfrage; der Worker erledigt die aufwendige Arbeit.
5. Redis kann Sessions speichern
Die Sessionverwaltung eignet sich ebenfalls dafür. Eine Schlüssel wie session:abc123 kann Folgendes enthalten:
{
"userId": "123",
"role": "ADMIN"
}
Dies wird wertvoll, wenn mehrere Backend-Instanzen über Redis denselben Session-Speicher teilen.
Wichtiger Unterschied: Redis macht die Authentifizierung nicht automatisch sicher.
Die Teams müssen weiterhin Session-Identifikatoren, sichere Cookies, Ablaufzeiten, gegebenenfalls CSRF-Schutz, Authentifizierung und Autorisierung handhaben.
Redis ist Infrastruktur, keine Sicherheitsstrategie.
6. Redis kann bei Echtzeitfunktionen helfen
Pub/Sub kann Ereignisse auf verschiedene Instanzen verteilen. Ein möglicher Ablauf sieht so aus: Ein Client ruft Server A auf, sendet eine Meldung an Redis, ein Abonnent auf Server B verarbeitet die Meldung und leitet sie anschließend an einen anderen Client weiter.
Illustration:
await redis.publish(
"notifications",
JSON.stringify({
userId: "123",
message: "Your order has shipped",
})
);
await subscriber.subscribe("notifications")
subscriber.on("message", (channel, message) => {
console.log(channel, message);
});
Nützlich für Benachrichtigungen, Echtzeit-Updates, chatbezogene Abläufe sowie die Verbreitung von Ereignissen.
Beschränkung: Redis Pub/Sub ist keine zuverlässige Nachrichtenwarteschlange.
Falls zuverlässige Verarbeitung, Wiederholungsversuche oder garantierte Zustellung wichtig sind, sollte je nach Fall lieber eine Warteschlange oder Redis Streams verwendet werden.
Es ist wichtig, diesen Unterschied zu kennen.
7. Redis wird zum Problem, wenn es überall eingesetzt wird
Die wichtigste Lektion: Sobald Redis verfügbar ist, besteht die Versuchung, alles dort zu speichern.
Tun Sie das nicht.
Allein die Geschwindigkeit bedeutet noch nicht, dass jedes Datum im Speicher gehören muss.
PostgreSQL kann weiterhin die dauerhafte Quelle der Wahrheit für Geschäftsdaten bleiben, während Redis für Caching, Sessions, OTPs, Rate Limits und Warteschlangen zuständig ist.
Eine praktische Aufteilung bewahrt langlebige Geschäftsdaten in PostgreSQL auf und reserviert Redis für häufig genutzte Bereiche, kurze TTL-Werte sowie unterstützende Arbeitslasten wie Warteschlangen oder Zähler.
Durch eine frühzeitige Abgrenzung werden viele spätere Umgestaltungen vermieden.
Redis-Datensstrukturen sind wichtig
Redis ist nicht nur Key → String. Es bietet mehrere Strukturen.
Strings
Einfache Werte, wie zum Beispiel user:123:name → „Mit“.
Hashes
Mehrere Felder unter einem Schlüssel:
user:123
name → Mit
role → ADMIN
email → example@email.com
Listen
Geordnete Sammlungen sowie einige warteschlangenähnliche Muster.
Sets
Einzigartige Werte.
Gesortete Sets
Elemente, die nach ihrem Wert geordnet sind – beispielsweise eine Leaderboard:
1000 → Player A
900 → Player B
800 → Player C
Durch die richtige Auswahl der Struktur wird das Problem oft vereinfacht.
Häufige Redis-Fehler, die man vermeiden sollte
Fehler 1: Alles zu cachen
Nicht jede Abfrage benötigt einen Cache. Das Caching erhöht die Komplexität. Wenn eine Abfrage bereits schnell genug ist, kann Redis ein Problem lösen, das gar nicht existiert.
Fehler 2: Keine Ablaufzeit
Temporäre Daten ohne TTLs sammeln sich an. Wenn die Daten nicht ewig bestehen müssen, sollte eine Ablaufzeit festgelegt werden.
Fehler 3: Redis als permanente Datenbank betrachten
Falls Redis die einzige Kopie kritischer Geschäftsdaten enthält, besteht eine ernsthafte Abhängigkeit des Systems. Man muss den Quellenwert kennen.
Fehler 4: Redis-Fehler ignorieren
Besprechen Sie, was passiert, wenn Redis ausfällt. Bei vielen Cache-Anwendungen ist ein Rückgriff auf die Datenbank akzeptabel. Die richtige Strategie hängt von der Rolle ab, die Redis spielt.
Fehler 5: Redis ohne Verständnis der Arbeitslast verwenden
Das Fast-Limit ist nicht unendlich. Man muss weiterhin über Speicher, Ausschließung von Daten, Verbindungen, Schlüsseldesign, TTL-Werte, Serialisierung, Netzwerkverzögerungen sowie Anforderungen an die Persistenz nachdenken.
Wie Redis das Denken im Backend verändert
Viele Designs beginnen mit einem Anfragenverarbeiter, der direkt mit SQL kommuniziert.
Sobald ein In-Memory-Speicher sowie verzögert arbeitende Prozesse hinzukommen, verlängert sich der Ablauf oft: Der Verarbeiter prüft zunächst Redis und danach die Datenbank; oder er stellt Aufgaben in eine Warteschlange, die von einem Prozess bearbeitet wird, der mit einem externen Anbieter kommuniziert.
Backends bestehen aus einer Sammlung spezialisierter Komponenten. Redis ist eine solche Komponente, die in mehreren Rollen vorkommen kann.
Fazit
Nehmen Sie Redis nicht einfach deshalb an, weil alle anderen es tun.
Schauen Sie sich diese Ansätze an, wenn die Anforderungen klar sind: Caching für häufig gelesene Daten, TTL-Schlüssel für kurzlebige Geheimnisse, Zähler zur Begrenzung von Missbrauch, Warteschlangen für verzögerte Aufgaben, ein gemeinsamer Session-Speicher über Instanzen hinweg oder Pub/Sub, wenn eine leichte Verteilung ausreicht.
Vermeiden Sie es, Redis zur Standardlösung für jede Backend-Frage zu machen.
Gute Systeme sind nicht diejenigen mit den längsten Tool-Listen. Es sind diejenigen, bei denen jedes Tool seinen Platz verdient hat.