In-Memory Store

Redis

Redis ist ein In-Memory-Datenstruktur-Server: Keys werden auf Strings, Hashes, Listen, Sets, Sorted Sets und Streams gemappt, die über einen Single-Threaded-Command-Loop aus dem RAM bereitgestellt werden.

intermediate14 min readUpdated 16. Sept. 2026
redis-cli
bash
# redis-cli
SET session:9f2c '{"userId":42}' EX 3600

HSET cart:42 sku:KB-01 1 sku:MS-02 2
ZADD leaderboard 1500 ada 1420 grace
ZREVRANGE leaderboard 0 9 WITHSCORES

INCR rate:203.0.113.7:2026091612
EXPIRE rate:203.0.113.7:2026091612 60
Veröffentlicht
2009
Datenmodell
In-memory key-value
Geschrieben in
C
Datenstrukturen
Strings, hashes, lists, sets, sorted sets, streams
Persistenz
RDB + AOF
Lizenz
AGPLv3 (Redis 8+)
Version
8.x

Warum es wichtig ist

Warum Redis der Standard-Cache ist

Latenz im Mikrosekundenbereich

Da der Datensatz im RAM liegt und die Befehle simpel sind, erfolgen Lese- und Schreibvorgänge auf gewöhnlicher Hardware in weit unter einer Millisekunde.

Datenstrukturen, nicht nur Strings

Hashes, Listen, Sets, Sorted Sets und Streams sind First-Class-Citizens, sodass Counter, Queues und Rankings integriert sind und nicht erst mühsam implementiert werden müssen.

Replikation und Clustering

Replicas, Sentinel-Failover und Redis Cluster ermöglichen es, eine einzelne Instanz zu einem hochverfügbaren, sharded Deployment auszubauen.

Das Gesamtbild

Die drei Grundideen hinter Redis

Alles ist ein Key, der Value besitzt eine Datenstruktur und jeder Befehl wird nacheinander in einem einzigen Thread ausgeführt.

Keys

Adresse

Jeder Value wird unter einem String-Key gespeichert. Ein klares Benennungsschema ist das Äquivalent zu einem Schema in Redis.

TTL & Eviction

Bereinigung

Keys können nach einer bestimmten Zeit oder einer Phase der Inaktivität ablaufen, und eine Memory-Policy entscheidet, was gelöscht wird, wenn die Instanz voll ist.

Commands

Operation

Operationen sind kleine atomare Befehle wie GET, HSET und ZADD, die entweder aus deiner Anwendung aufgerufen oder in einer Pipeline gruppiert werden.

Datenmodell

Wie Keys und Values aufgebaut sind

Eine Redis-Datenbank ist eine Map von String-Keys zu typisierten Values, die je nach Anwendungsfall gewählt werden.

Keys und die Strukturen dahinterKey-value map
  • session:{id}hashSession-Felder mit einer sliding TTL
  • rate:{ip}:{minute}stringCounter für ein Fixed-Window Rate Limit
  • leaderboardsorted setMit Punkten bewertete Mitglieder, gerankt mit ZREVRANGE
  • queue:emailslistLPUSH / BRPOP Work Queue
  • user:{id}:followerssetEindeutige Follower-IDs ohne Duplikate
  • eventsstreamAppend-only Log-Read mit Consumer Groups

Redis speichert jeden Value unter einem String-Key; der Value-Typ wird je nach Anwendungsfall gewählt.

Eine kurze Geschichte

Vom Echtzeit-Logger zur Datenplattform

  1. 2009

    Redis wird erschaffen

    Salvatore Sanfilippo entwickelt Redis, um ein Echtzeit-Analytics-Produkt zu beschleunigen, und stellt es anschließend als Open Source zur Verfügung.

    09
  2. 2010

    VMware übernimmt die Betreuung

    Das Projekt erhält Vollzeit-Maintainer, und Redis 2.0 fügt Hashes, Pub/Sub und Replikation hinzu.

    10
  3. 2012

    Lua-Scripting

    Redis 2.6 führt serverseitiges Lua ein, wodurch mehrere Befehle atomar in einem einzigen Roundtrip ausgeführt werden können.

    12
  4. 2015

    Redis Cluster erscheint

    Redis 3.0 führt automatisches Sharding über Knoten hinweg sowie ein überarbeitetes Replikationsprotokoll ein.

    15
  5. 2018

    Streams kommen hinzu

    Redis 5.0 fügt eine Log-ähnliche Datenstruktur mit Consumer Groups hinzu und wird damit zu einem leistungsfähigen Message Broker.

    18
  6. 2020

    Sicherheit und Threads

    Redis 6 führt ACLs, TLS und threaded I/O ein, während RESP3 das Protokoll modernisiert.

    20
  7. 2025

    Redis 8 und AGPL

    Nach einer Lizenzänderung im Jahr 2024 kehrt Redis 8 zu einer Open-Source-Lizenz zurück und bündelt die ehemaligen Stack-Module.

    25

Der vollständige Leitfaden

Redis: Alles was Sie wissen müssen

Was ist Redis?

Redis ist ein In-Memory-Datenstruktur-Server. Er hält seinen gesamten Datensatz im RAM und beantwortet Befehle innerhalb von Mikrosekunden. Jeder Wert wird unter einem String-Key gespeichert, wobei der Wert ein String, ein Hash, eine List, ein Set, ein Sorted Set, ein Stream, eine Bitmap oder ein HyperLogLog sein kann.

Es startete 2009 als Lösung, um ein Echtzeit-Analytics-Produkt zu beschleunigen, und wurde schnell zum Standard-Cache für das Web. Der Grund dafür ist nicht nur die Geschwindigkeit. Redis bietet zudem einen kompakten, komponierbaren Befehlssatz, nützliche Datenstrukturen, TTLs, Replikation und Scripting – genug, dass Teams es für weit mehr als nur Caching einsetzen.

Betrachten Sie Redis als eine gemeinsame In-Memory-Datenstruktur, die für jeden Prozess sichtbar ist. Diese Perspektive erklärt sowohl seine Stärken als auch seine Grenzen.

Warum Redis so schnell ist

Drei Faktoren machen Redis schnell, und keiner davon ist ein Geheimnis.

Erstens: Die Daten liegen im Arbeitsspeicher. Es gibt keinen Disk-Seek und keinen Buffer-Pool, der auf dem Lesepfad verwaltet werden muss. Speicherzugriffe werden in Nanosekunden gemessen; ein Netzwerk-Roundtrip in Bruchteilen einer Millisekunde.

Zweitens: Es gibt keinen Query-Planner. Ein Befehl wie GET user:42 ist ein einfacher Hash-Table-Lookup, und ZADD ist ein Skip-List-Insert. Es gibt kein Parsen einer Abfragesprache, keinen Optimierungsschritt und keine Joins. Die Operation ist fest definiert und direkt.

Drittens: Befehle werden nacheinander in einem einzigen Thread ausgeführt. Das klingt zunächst wie eine Schwäche, eliminiert aber Lock-Contention und macht jeden Befehl atomar. Der Event-Loop verwaltet tausende Verbindungen und führt Befehle sequenziell aus, weshalb eine einzige Instanz einen enormen Durchsatz bewältigen kann.

Der Haken dabei ist, dass ein einziger langsamer Befehl alles blockiert. KEYS * über Millionen von Keys oder ZRANGE über ein riesiges Sorted Set bringt jeden anderen Client zum Stillstand. Halten Sie Befehle daher begrenzt und verwenden Sie in der Produktion SCAN anstelle von KEYS.

Die Datenstrukturen, auf die es ankommt

Die Wahl der richtigen Struktur macht den Großteil des Könnens aus. Hier ist der Einsatzzweck der einzelnen Typen.

Strings speichern Text, JSON, Zähler und Binärdaten. SET, GET, INCR, APPEND und SETEX sind hier die Arbeitstiere.

SET user:42:name "Ada"
INCR page:home:views
SETEX token:abc123 900 "opaque-token-value"

Hashes speichern ein Objekt als Feld-Wert-Paare unter einem einzigen Key. Sie sind perfekt für Sessions und Datensätze, die feldweise aktualisiert werden, da so die Serialisierung des gesamten Objekts bei jedem Schreibvorgang vermieden wird.

HSET session:9f2c userId 42 role admin
HINCRBY session:9f2c pageViews 1
HGET session:9f2c role

Lists sind geordnete Sequenzen mit O(1) Push- und Pop-Operationen an beiden Enden. Sie eignen sich für einfache Queues, Activity-Feeds und Capped Logs.

LPUSH queue:emails "welcome:42"
RPOP queue:emails
LTRIM feed:global 0 99

Sets enthalten eindeutige, ungeordnete Elemente. Nutzen Sie diese für Tags, Follower und Mitgliedschaftstests.

SADD post:7:tags redis database
SISMEMBER post:7:tags redis
SINTER user:1:follows user:2:follows

Sorted sets sind das Highlight. Jedes Element hat einen Score, sodass das Set nach diesem Score sortiert bleibt, während Lookups bei O(log n) liegen. Leaderboards, Priority Queues und Rate-Limit-Fenster basieren darauf.

ZADD leaderboard 1500 ada
ZINCRBY leaderboard 50 ada
ZREVRANGE leaderboard 0 9 WITHSCORES

Streams sind ein Append-only Log mit Consumer Groups und liegen funktional näher an Kafka als an einer Liste. Nutzen Sie diese, wenn Sie Replay, Acknowledgement und mehrere Consumer benötigen.

XADD events * type signup userId 42
XREADGROUP GROUP workers alice COUNT 10 STREAMS events ">"

Bitmaps und HyperLogLogs übernehmen spezialisierte Zählaufgaben. Bitmaps tracken pro Benutzer Booleans mit jeweils einem Bit – ideal für Daily Active Users; HyperLogLog schätzt die Anzahl eindeutiger Elemente mit etwa 12 KB und einer geringen Fehlerrate.

SETBIT active:2026-09-16 42 1
PFADD visitors:2026-09-16 user:42
PFCOUNT visitors:2026-09-16

Wenn Sie sich eine Regel merken, dann diese: Wählen Sie die Struktur, die zum Zugriffsmuster passt, und nicht die, die zu Ihrem bereits vorhandenen JSON passt.

Key-Benennung und Namespacing

Redis besitzt keine Tabellen, keine Schemas und keine Namespaces. Die einzige Struktur ist der Key selbst, daher ist Ihre Benennungskonvention gleichzeitig Ihr Schema.

Ein gängiges Muster ist object:id:attribute in durch Doppelpunkte getrennten Segmenten:

user:42
user:42:sessions
session:9f2c
order:1001:items
rate:203.0.113.7:2026091612

Kurze, vorhersehbare Keys halten den Speicherbedarf niedrig und erleichtern das Scannen sowie das Debugging. Fügen Sie ein Versions- oder Environment-Präfix hinzu, wenn sich mehrere Anwendungen eine Instanz teilen: app:v2:user:42. Vermeiden Sie Keys, die aus unbegrenzten Benutzereingaben abgeleitet sind, und verwenden Sie niemals Leerzeichen oder Zeilenumbrüche.

Da Keys Strings sind, können Sie diese zum Debugging auflisten, sollten dies jedoch vorsichtig tun. SCAN mit einem Cursor ist sicher; KEYS hingegen nicht, da es den Server blockiert, während der gesamte Keyspace durchlaufen wird.

Ablaufdatum, TTL und Eviction

Das Ablaufdatum (Expiry) ist das, was Redis zu einem Cache macht und verhindert, dass es zu einem Memory Leak wird. Fast jeder Key, den Sie für das Caching erstellen, sollte eine TTL haben.

SET user:42 '{"name":"Ada"}' EX 300   # seconds
SET user:42 '{"name":"Ada"}' PX 300000 # milliseconds
EXPIRE user:42 300
TTL user:42
PERSIST user:42

EXPIRE und seine Varianten legen ein Timeout fest; TTL gibt die verbleibenden Sekunden zurück. Das Ablaufdatum wird sowohl “lazy” als auch aktiv verwaltet: Keys werden entfernt, wenn sie nach ihrem Ablaufdatum aufgerufen werden, und ein Hintergrundzyklus prüft stichprobenartig und löscht abgelaufene Keys, sodass der Speicher auch dann wieder freigegeben wird, wenn sie nie wieder gelesen werden.

TTLs allein reichen nicht aus, wenn der Datensatz schneller wächst, als er abläuft. Setzen Sie maxmemory und eine maxmemory-policy:

  • noeviction — Schreibvorgänge werden abgelehnt, wenn der Speicher voll ist; der sichere Standard für persistente Daten.
  • allkeys-lru — entfernt den am wenigsten kürzlich verwendeten Key (Least Recently Used) unabhängig vom Typ; die gängige Wahl für Caches.
  • allkeys-lfu — entfernt den am wenigsten häufig verwendeten Key (Least Frequently Used), besser geeignet, wenn ein kleiner Satz an “Hot Keys” entscheidend ist.
  • volatile-lru / volatile-ttl — entfernt nur Keys, die ein Ablaufdatum haben, sodass persistente Keys geschützt bleiben.
CONFIG SET maxmemory 2gb
CONFIG SET maxmemory-policy allkeys-lru

Die Wahl ist entscheidend. allkeys-lru wird problemlos eine Session entfernen, wenn dies der “kälteste” Key ist; volatile-ttl wird dies nicht tun, da Sessions immer ein Ablaufdatum haben.

Caching-Patterns und Invalidierung

Das gängigste Pattern ist cache-aside. Die Anwendung prüft zuerst Redis, greift bei einem Miss auf die Datenbank zurück und schreibt das Ergebnis mit einer TTL zurück.

async function getUser(id) {
  const key = `user:${id}`;
  const cached = await redis.get(key);
  if (cached) return JSON.parse(cached);

  const user = await db.users.findById(id);
  if (user) await redis.set(key, JSON.stringify(user), { EX: 300 });
  return user;
}

Zwei weitere Patterns sind wichtig zu kennen. Write-through aktualisiert den Cache und die Datenbank gleichzeitig, was die Lesezugriffe beschleunigt, aber den Schreibpfad verdoppelt. Write-behind schreibt zuerst in Redis und spült die Daten asynchron in die Datenbank; das ist schnell, birgt aber das Risiko von Datenverlusten bei Schreibvorgängen.

Die Invalidierung ist der schwierige Teil. Eine TTL garantiert eine schließliche Korrektheit; explizite Löschvorgänge machen sie unmittelbar. Eine zuverlässige Gewohnheit ist es, zuerst die Datenbank zu aktualisieren und anschließend den Cache-Key zu löschen, wobei man ein kurzes Zeitfenster akzeptiert, in dem ein gleichzeitiger Leser veraltete Daten erneut in den Cache schreibt.

async function updateUser(id, patch) {
  const user = await db.users.update(id, patch);
  await redis.del(`user:${id}`);
  return user;
}

Widerstehe der Versuchung, einen Cache ewig beizubehalten, nur weil die Invalidierung lästig ist. Veraltete Daten und unbegrenzter Speicherverbrauch sind weitaus größere Probleme als eine geringfügig niedrigere Hit-Rate.

Sessions, Rate Limiting und Leaderboards

Redis ist immer dann die beste Wahl, wenn Zustände über Prozesse hinweg geteilt werden müssen und Neustarts überstehen sollen.

Sessions sind ein Hash oder ein serialisierter String mit einer TTL, die sich bei jeder Anfrage aktualisiert (sliding window).

await redis.set(`session:${sid}`, JSON.stringify(data), { EX: 3600 });
await redis.expire(`session:${sid}`, 3600); // sliding window

Rate Limiting ist ein Zähler mit einem Ablaufdatum. Ein festes Zeitfenster (fixed window) erfordert zwei Befehle; ein gleitendes Zeitfenster (sliding window) nutzt ein Sorted Set aus Zeitstempeln.

const minute = Math.floor(Date.now() / 60_000);
const key = `rate:${ip}:${minute}`;
const replies = await redis.multi().incr(key).expire(key, 60).exec();
if (replies[0] > 100) throw new Error("rate_limited");

Leaderboards sind ein Sorted Set. ZADD zeichnet einen Score auf, ZINCRBY passt diesen an und ZREVRANGE gibt die Top-Spieler inklusive ihrer Scores zurück. ZRANK liefert die Position eines einzelnen Spielers – genau das, was für eine Profilseite benötigt wird.

ZADD leaderboard 1500 ada
ZINCRBY leaderboard 50 ada
ZREVRANGE leaderboard 0 9 WITHSCORES
ZRANK leaderboard ada

Alle drei Muster basieren auf denselben zwei Eigenschaften: Befehle sind atomar und jeder Key kann ablaufen.

Queues und Pub/Sub

Listen eignen sich hervorragend als zuverlässige Work-Queue. Producer LPUSH, Worker BRPOP — durch das blockierende Pop-Verfahren schläft ein Worker, bis ein Job eintrifft, anstatt ständig zu pollen.

LPUSH queue:emails "welcome:42"
BRPOP queue:emails 30

Für eine zuverlässige Queue sollten Jobs in eine Processing-Liste verschoben werden, bevor sie bearbeitet werden, und erst nach erfolgreichem Abschluss wieder entfernt werden. So wird verhindert, dass ein abgestürzter Worker einen Job stillschweigend verliert.

Pub/Sub ist ein Fire-and-Forget-Messaging: Publisher PUBLISH senden Nachrichten, Subscriber empfangen sie über Channels. Dies ist ideal für Live-Benachrichtigungen und Cache-Busting-Signale, bietet jedoch keine Persistenz und keine Zustellgarantie. Wenn ein Subscriber offline ist, ist die Nachricht verloren.

PUBLISH notifications:user:42 "You have a new follower"
SUBSCRIBE notifications:user:42

Wenn Sie Persistenz, Bestätigungen (Acknowledgements) und Replay-Funktionen benötigen, verwenden Sie stattdessen Streams mit Consumer-Gruppen. Streams sind die moderne Antwort auf die Anforderung: „Ich möchte Pub/Sub, aber zuverlässig“.

Pipelines, Transactions und Lua

Ein Befehl entspricht einem Netzwerk-Roundtrip. Wenn Sie zehn Werte benötigen, verbringen zehn sequentielle GETs die meiste Zeit mit dem Warten auf das Netzwerk. Eine Pipeline sendet diese gemeinsam und liest alle Antworten auf einmal aus.

const [name, plan, logins] = await redis
  .multi()
  .hget("user:42", "name")
  .hget("user:42", "plan")
  .hget("user:42", "logins")
  .exec();

Eine Pipeline ist nicht automatisch eine Transaction. Das Umschließen von Befehlen mit MULTI/EXEC sorgt dafür, dass diese als ein atomarer Block ausgeführt werden, ohne dass Befehle anderer Clients dazwischengeschoben werden. Redis führt bei einem Befehlsfehler keinen Rollback durch, wie es SQL tut; es macht einfach weiter. Validieren Sie daher die Eingaben, bevor Sie diese in die Queue stellen.

Für Logik, die atomar auf dem Server ausgeführt werden muss, verwenden Sie ein Lua-Script. Es wird als einzelner Befehl ausgeführt, kann mehrere Keys lesen und schreiben und ist das richtige Werkzeug für Compare-and-Set-Operationen – zum Beispiel, um einen Lock nur dann freizugeben, wenn man ihn noch besitzt.

const release = `
  if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
  end
  return 0
`;

await redis.eval(release, { keys: ["lock:order:1001"], arguments: [token] });

Scripts sollten klein und deterministisch sein. Führen Sie innerhalb eines Scripts nichts Unbegrenztes aus, da der gesamte Server auf dessen Abschluss wartet.

Persistenz: RDB und AOF

In-memory muss nicht zwangsläufig flüchtig bedeuten, aber Durability ist ein Trade-off, den man bewusst wählen muss.

RDB schreibt Point-in-Time-Snapshots des Datensatzes auf die Festplatte, entweder nach einem Zeitplan oder auf Anfrage. Snapshots sind kompakt, lassen sich schnell wiederherstellen und sind ideal für Backups. Der Nachteil ist, dass bei einem Absturz alle Daten verloren gehen, die seit dem letzten Snapshot geschrieben wurden.

SAVE     # blocking snapshot, avoid in production
BGSAVE   # fork and snapshot in the background

AOF hängt jeden Schreibbefehl an ein Log an. Mit appendfsync everysec verliert man maximal etwa eine Sekunde an Schreibvorgängen; mit always verliert man fast nichts, zahlt dafür aber einen deutlich höheren Preis bei der Schreibperformance. AOF-Dateien können im Hintergrund neu geschrieben werden, um kompakt zu bleiben.

CONFIG SET appendonly yes
CONFIG SET appendfsync everysec

In vielen Deployments werden beide Optionen aktiviert: RDB für schnelle Wiederherstellungen und AOF für ein minimales Verlustfenster. Wenn Sie Redis rein als Cache verwenden, können Sie die Persistenz komplett deaktivieren und einen kalten Cache einfach aus der Datenbank neu befüllen lassen. Wenn Sie Redis für Queues oder wichtige Counter nutzen, lassen Sie die Persistenz aktiviert und wissen Sie genau, wie viele Daten bei einem Absturz verloren gehen könnten.

Replication, Sentinel und Cluster

Eine Replica verbindet sich mit einem Primary und empfängt einen Stream von Schreibvorgängen, sodass sie eine Kopie in nahezu Echtzeit hält. Replicas können Lesezugriffe bedienen, was den Primary entlastet, und sie bilden die Grundlage für das Failover.

Sentinel überwacht einen Primary sowie dessen Replicas und befördert eine Replica zum Primary, falls der Primary ausfällt. Zudem teilt Sentinel den Clients die Adresse des neuen Primaries mit. Sentinel bietet Hochverfügbarkeit für Datensätze, die auf einen einzigen Node passen.

Redis Cluster sharded Daten über mehrere Nodes hinweg mithilfe von 16.384 Hash-Slots. Jeder Key wird durch das Hashen seines Namens einem Slot zugeordnet, und jeder Node besitzt einen bestimmten Bereich dieser Slots. Clients können mit jedem beliebigen Node kommunizieren und werden dann an den richtigen Node weitergeleitet. Cluster ermöglicht horizontale Skalierung und Failover, allerdings auf Kosten der Komplexität: Multi-Key-Operationen müssen Keys im selben Slot halten, üblicherweise durch Hash-Tags wie user:{42}:name.

Der übliche Pfad führt von einer einzelnen Instanz über eine Replica und Sentinel hin zu Cluster – und das erst dann, wenn der Speicher nicht mehr auf eine einzige Maschine passt.

Häufige Anti-Patterns

  • Redis als einzige Datenbank. Ohne eine auf Haltbarkeit (Durability) abgestimmte Persistenz und Replikation können bei einem Absturz Daten verloren gehen. Behalten Sie ein dauerhaftes System of Record bei.
  • Unbegrenzte Keys. Jeder gecachte Key benötigt eine TTL, andernfalls füllt sich der Speicher und die Eviction beginnt, Dinge zu löschen, die Sie eigentlich benötigen.
  • Große Keys und große Collections. Ein einzelner Hash mit Millionen von Feldern oder eine Liste mit Millionen von Einträgen lässt sich nur langsam löschen und kann den Server blockieren.
  • KEYS in der Produktion. Dies blockiert den Event Loop. Verwenden Sie SCAN.
  • Ein Key für alles. Das Serialisieren eines gesamten Objekts bei jeder Änderung führt zu Write Amplification. Nutzen Sie stattdessen Hashes und aktualisieren Sie einzelne Felder.
  • Ignorieren der Eviction Policy. allkeys-lru kann auch Sessions und Locks löschen, nicht nur Cache-Einträge.

Verbindung über Node

Zwei Clients dominieren das Node-Ökosystem: node-redis und ioredis. Beide sprechen dasselbe Protokoll und stellen pro Befehl eine Methode bereit, sodass die Entscheidung meist eine Frage des API-Geschmacks und der Unterstützung für Clustering ist.

import { createClient } from "redis";

const redis = createClient({ url: process.env.REDIS_URL });
redis.on("error", (err) => console.error("redis", err));
await redis.connect();

await redis.set("health", "ok", { EX: 60 });
const value = await redis.get("health");

Erstellen Sie den Client einmal beim Start und teilen Sie ihn. Eine Verbindung ist ein Socket mit einer Befehlswarteschlange; das Öffnen einer Verbindung pro Request erhöht die Latenz und verbraucht unnötig File-Deskriptoren. Binden Sie immer einen error-Handler ein: Ohne diesen kann eine unterbrochene Verbindung als nicht behandelte Exception (unhandled exception) auftreten.

Die meisten Befehle geben einfache Werte zurück, Optionen werden jedoch als Objekt übergeben. SET akzeptiert { EX: 300 } für Sekunden, { NX: true } um nur zu schreiben, wenn der Key nicht vorhanden ist, und { KEEPTTL: true } um ein bestehendes Expiry zu beibehalten. NX zusammen mit einem TTL ist das Standardrezept für einen Distributed Lock.

const acquired = await redis.set(`lock:order:${id}`, token, {
  NX: true,
  EX: 30,
});

Wählen Sie für ein sharded Deployment einen Client, der Cluster-Weiterleitungen und Hash-Tags versteht. Beide großen Clients unterstützen dies; entscheidend ist, verwandte Keys im selben Slot zu halten, wenn ein Befehl mehr als einen Key anspricht.

Eine laufende Instanz überwachen

Einige wenige Befehle verraten fast alles über den Zustand einer Redis-Instanz.

INFO memory
INFO stats
DBSIZE
SLOWLOG GET 10

INFO memory berichtet über used_memory, maxmemory und die aktuelle Policy. INFO stats enthält keyspace_hits und keyspace_misses; deren Verhältnis ergibt die Cache-Hit-Rate – die entscheidende Kennzahl bei der Optimierung von TTLs. DBSIZE zählt die Keys, und SLOWLOG protokolliert Befehle, die einen Latenzschwellenwert überschritten haben.

CONFIG SET slowlog-log-slower-than 10000  # microseconds
CONFIG RESETSTAT

Zwei weitere Tools sind nützlich, aber riskant. MONITOR streamt jeden Befehl, den der Server verarbeitet, in Echtzeit; das ist unschätzbar wertvoll für das Debugging, aber zu ressourcenintensiv, um es in der Produktion aktiviert zu lassen. SCAN durchläuft den Keyspace in cursor-großen Batches und ist sicher, im Gegensatz zu KEYS.

Behalten Sie drei Werte auf Ihrem Dashboard im Auge: die Speicherauslastung im Verhältnis zu maxmemory, die Anzahl der Evictions und die Hit-Rate. Eine sinkende Hit-Rate bei gleichzeitig steigenden Evictions bedeutet meist, dass die TTLs zu kurz sind oder der Datensatz nicht mehr hineinpasst. In diesem Fall ist es besser, den Arbeitsspeicher zu erweitern oder die Keys zu optimieren, anstatt das Limit zu erhöhen und die Instanz in den Swap-Zustand laufen zu lassen.

Best Practices

  • Legen Sie für jeden gecachten Key eine TTL fest und wählen Sie eine Eviction Policy, die zu den Daten passt.
  • Verwenden Sie ein konsistentes object:id:attribute Benennungsschema und dokumentieren Sie dieses.
  • Wählen Sie die Datenstruktur, die zum Zugriffsmuster passt, bevor Sie Code schreiben.
  • Verwenden Sie eine einzige Client-Verbindung wiederholt und nutzen Sie Pipelining für unabhängige Befehle.
  • Verwenden Sie Lua oder MULTI für atomare mehrstufige Operationen und halten Sie diese kurz.
  • Bevorzugen Sie SCAN gegenüber KEYS und begrenzen Sie die Größe jedes einzelnen Keys.
  • Aktivieren Sie Persistenz und Replikation für Daten, die nicht neu erstellt werden können, und testen Sie die Wiederherstellung.
  • Überwachen Sie den Speicher, die Hit Rate, Evictions und langsame Befehle, bevor diese zu Vorfällen führen.

Häufige Fehler

  • Caching ohne TTL, wodurch der Speicher langsam aufgebraucht wird.
  • Verwendung von KEYS * zum Suchen von Keys, was alle anderen Clients blockiert.
  • Pub/Sub als zuverlässige Queue behandeln und Nachrichten verlieren, wenn ein Subscriber offline ist.
  • Ein großes Objekt als einen einzigen JSON-String speichern und diesen bei jeder kleinen Änderung komplett neu schreiben.
  • Die Annahme, dass MULTI wie eine SQL-Transaktion ein Rollback durchführt; das tut es nicht.
  • Zulassen, dass ein einzelner Hot Key oder ein riesiges Sorted Set zum Flaschenhals wird.
  • Vergessen, dass allkeys-lru Sessions, Locks und Rate-Limit-Counter evicten kann.
  • Redis ohne Persistence und ohne Replica betreiben und dadurch beim Neustart Daten verlieren.

Wie geht es weiter?

Redis ist die praktische Umsetzung von Caching, und wenn man es versteht, werden die Caching-Patterns auf der Backend-Roadmap greifbar. Kombiniere es mit einem dauerhaften System of Record: PostgreSQL für relationale Daten oder MongoDB für Dokumente. Verbinde es anschließend mit einem Node-Service und schau dir die Node.js-Grundlagen noch einmal an, um zu sehen, wie der Client in deinen Request-Pfad passt.

In der Praxis

Vier Patterns, die du ständig nutzen wirst

Ein Cache-Read, ein Hash-basiertes Objekt, ein Leaderboard und ein Rate Limiter decken einen Großteil der realen Redis-Nutzung ab.

cache.js
import { createClient } from "redis";

const redis = createClient({ url: process.env.REDIS_URL });
await redis.connect();

async function getUser(id) {
  const key = `user:${id}`;
  const cached = await redis.get(key);
  if (cached) return JSON.parse(cached);

  const user = await db.users.findById(id);
  if (user) {
    await redis.set(key, JSON.stringify(user), { EX: 300 });
  }
  return user;
}

Caching mit Ablaufdatum

Eine TTL begrenzt die Veralterung von Daten und gibt automatisch Speicher frei. Ein Key ohne Ablaufdatum bleibt bestehen, bis jemand an das Löschen denkt.

Bevorzugt
await redis.set(
  key,
  JSON.stringify(user),
  { EX: 300 },
);
Vermeiden
// No expiry: stale data lives
// until the key is deleted.
await redis.set(key, JSON.stringify(user));

Pipelining von Roundtrips

Jeder Befehl ist ein Netzwerk-Roundtrip. Gruppiere unabhängige Befehle in einer Pipeline, damit sie gemeinsam übertragen werden.

Bevorzugt
const [a, b, c] = await redis
  .multi()
  .get("a")
  .get("b")
  .get("c")
  .exec();
Vermeiden
const a = await redis.get("a");
const b = await redis.get("b");
const c = await redis.get("c");
// three sequential round trips

Abwägungen

Wann Redis die richtige Wahl ist

Redis ist ein herausragender Cache und eine exzellente Koordinationsschicht, aber kein Ersatz für eine dauerhafte Primärdatenbank.

Strengths

  • Geschwindigkeit, die Designs verändert

    Lesezugriffe im Mikrosekundenbereich machen Sessions, Rate Limiting und Leaderboards direkt im Request-Pfad praktikabel.

  • Ein Tool, viele Strukturen

    Counter, Queues, Sets und Rankings sind integriert, sodass du nicht für jede Funktion einen separaten Service betreiben musst.

  • Einfacher Betrieb

    Ein einziger Prozess, ein kleiner Befehlssatz und ausgereifte Clients machen Redis einfach zu betreiben und zu verstehen.

Trade-offs

  • Speicher ist das Limit

    Der gesamte Datensatz liegt im RAM, was pro Gigabyte deutlich teurer ist als auf der Festplatte. Die Eviction-Policy entscheidet, was verworfen wird.

  • Durability ist eine Option

    RDB-Snapshots und AOF reduzieren Datenverluste, eliminieren sie aber nie ganz. Ein Absturz kann immer noch die neuesten Schreibvorgänge löschen.

  • Single-threaded Commands

    Ein einziger langsamer Befehl, wie KEYS oder ein riesiger ZRANGE, blockiert jeden anderen Client. Halte Befehle klein und begrenzt.

Häufig gestellte Fragen

Häufig gestellte Fragen

Keep learning

Related topics from the roadmap.

$ Lernen Sie jetzt

Bereit, Redis zu lernen?

Unser interaktives Tutorial führt Sie Schritt für Schritt durch Redis — mit Quizzen und echtem Code, den Sie im Browser ausführen können.