Developer Tools

Redis jenseits des Cachings: Datenstrukturen, Pub/Sub und Echtzeit-Anwendungen

Lerne, wie du Redis als mehr als nur einen Cache einsetzt – entdecke Datenstrukturen, Pub/Sub-Messaging, Streams und Muster für den Aufbau schneller, skalierbarer Anwendungen.

10 Min. Lesezeit

Serverraum mit leuchtenden Verbindungen

Die meisten Entwickler kennen Redis als „diesen schnellen Cache". Doch Redis ist ein vollwertiger In-Memory-Datenstrukturserver, der Session-Management, Echtzeit-Bestenlisten, Rate-Limiting, Message-Queues und vieles mehr ermöglicht – alles mit Sub-Millisekunden-Latenz.

Warum Redis so schnell ist

Redis speichert alles im Arbeitsspeicher und verwendet einen Single-Threaded Event Loop zur Verarbeitung von Befehlen. Kein Disk-I/O bei Lesevorgängen, kein Lock-Contention, kein Context-Switching. Das Ergebnis: 100.000+ Operationen pro Sekunde auf handelsüblicher Hardware.

Typischer Latenzvergleich:
┌─────────────────┬──────────────┐
│ Operation       │ Latenz       │
├─────────────────┼──────────────┤
│ Redis GET       │ 0,1 ms       │
│ PostgreSQL SEL  │ 1-5 ms       │
│ REST API call   │ 50-200 ms    │
│ Disk read       │ 5-10 ms      │
└─────────────────┴──────────────┘

Kerndatenstrukturen

Redis ist nicht nur ein einfacher Key-Value-Speicher. Es unterstützt umfangreiche Datenstrukturen, die direkt auf häufige Anwendungsanforderungen zugeschnitten sind.

Strings – der Grundbaustein

Strings speichern Text, Zahlen oder Binärdaten bis zu 512 MB:

SET user:1001:name "Alice"
GET user:1001:name          # "Alice"

# Atomarer Zähler
INCR page:views             # 1, 2, 3, ...
INCRBY cart:total 2500      # 25,00 hinzufügen (in Cent gespeichert)

# Ablaufende Keys (TTL)
SET session:abc123 "{...}" EX 3600   # Läuft in 1 Stunde ab
TTL session:abc123                    # Verbleibende Sekunden

Hashes – leichtgewichtige Objekte

Hashes eignen sich hervorragend zum Speichern von Objekten ohne Serialisierungs-Overhead:

HSET user:1001 name "Alice" email "alice@example.com" plan "pro"
HGET user:1001 name         # "Alice"
HGETALL user:1001           # Alle Felder und Werte
HINCRBY user:1001 logins 1  # Atomares Feld-Inkrement

Hashes verbrauchen 10-mal weniger Speicher als das Speichern jedes Feldes als separaten Key.

Lists – Queues und Feeds

Geordnete Sammlungen, die Push/Pop von beiden Seiten unterstützen:

LPUSH notifications:alice "Neuer Kommentar zu deinem Beitrag"
LPUSH notifications:alice "Du hast einen neuen Follower"
LRANGE notifications:alice 0 9    # Neueste 10 Benachrichtigungen

# Als Queue verwenden (Producer/Consumer)
RPUSH queue:emails "send-welcome"
LPOP queue:emails                  # Nächsten Job verarbeiten

Sets – eindeutige Sammlungen

Ungeordnete Sammlungen einzigartiger Strings:

SADD tags:post:42 "redis" "database" "nosql"
SMEMBERS tags:post:42             # Alle Tags
SISMEMBER tags:post:42 "redis"    # true

# Set-Operationen
SINTER tags:post:42 tags:post:99  # Gemeinsame Tags
SUNION tags:post:42 tags:post:99  # Alle Tags zusammen

Sorted Sets – Bestenlisten und Rankings

Sets mit einem Score für jedes Element, automatisch sortiert:

ZADD leaderboard 1500 "alice" 2300 "bob" 1800 "charlie"
ZREVRANGE leaderboard 0 2 WITHSCORES   # Top-3-Spieler
ZRANK leaderboard "alice"               # Rang (0-basiert)
ZINCRBY leaderboard 100 "alice"         # Alice erzielt 100 Punkte

So werden Spiel-Bestenlisten, Trending-Beiträge und Priority-Queues in großem Maßstab realisiert.

Praxisnahe Muster

Muster 1: Session-Speicherung

Statt datenbankgestützter Sessions verwendet man Redis für sofortige Abfragen:

import redis
r = redis.Redis()

# Session speichern
r.setex(f"session:{token}", 3600, json.dumps(user_data))

# Session abrufen
data = r.get(f"session:{token}")

Warum? Datenbank-Sessions fügen 1–5 ms pro Anfrage hinzu. Redis-Sessions fügen 0,1 ms hinzu. Bei 1.000 Anfragen/s spart das 5 Sekunden pro Sekunde.

Muster 2: Rate-Limiting

Sliding-Window-Rate-Limiter mit 3 Befehlen:

# 100 Anfragen pro Minute pro Nutzer erlauben
MULTI
INCR rate:user:1001
EXPIRE rate:user:1001 60
EXEC

# Prüfen: wenn INCR-Ergebnis > 100, Anfrage ablehnen

Muster 3: Caching mit Cache-Aside

Das gängigste Caching-Muster:

def get_user(user_id):
    # 1. Cache prüfen
    cached = redis.get(f"user:{user_id}")
    if cached:
        return json.loads(cached)
    
    # 2. Cache-Miss → Datenbank abfragen
    user = db.query("SELECT * FROM users WHERE id = %s", user_id)
    
    # 3. Cache befüllen (läuft in 5 Minuten ab)
    redis.setex(f"user:{user_id}", 300, json.dumps(user))
    return user

Muster 4: Pub/Sub für Echtzeit-Events

Redis Pub/Sub ermöglicht Echtzeit-Messaging zwischen Services:

# Subscriber (lauscht auf Nachrichten)
SUBSCRIBE chat:room:42

# Publisher (sendet Nachrichten)
PUBLISH chat:room:42 "Hallo alle!"

Im Anwendungscode:

# Publisher
redis.publish("notifications", json.dumps({
    "type": "new_order",
    "order_id": 12345
}))

# Subscriber
pubsub = redis.pubsub()
pubsub.subscribe("notifications")
for message in pubsub.listen():
    handle_notification(message)

Muster 5: Distributed Locks

Race-Conditions über mehrere Server hinweg verhindern:

# Lock erwerben (NX = nur wenn nicht vorhanden, EX = ablaufen)
SET lock:invoice:gen "worker-1" NX EX 30

# Lock freigeben (nur wenn man Eigentümer ist — Lua-Script verwenden)

Redis Streams – Event-Sourcing und Message-Queues

Streams sind Redis' Antwort auf Kafka-artige Event-Logs:

# Events hinzufügen
XADD orders * user_id 1001 product "Widget" qty 3
XADD orders * user_id 1002 product "Gadget" qty 1

# Neueste Events lesen
XRANGE orders - + COUNT 10

# Consumer-Groups (mehrere Worker)
XGROUP CREATE orders processors $ MKSTREAM
XREADGROUP GROUP processors worker-1 COUNT 5 BLOCK 2000 STREAMS orders >

Streams bieten persistente, wiedergebbare Event-Logs mit Consumer-Groups – ideal für Microservices-Kommunikation.

Performance-Tipps

1. Pipelining für Massenoperationen nutzen

Statt 100 Round-Trips 100 Befehle auf einmal senden:

pipe = redis.pipeline()
for i in range(100):
    pipe.set(f"key:{i}", f"value:{i}")
pipe.execute()  # Einzelner Round-Trip

2. Die richtige Datenstruktur wählen

  • Zähler benötigt? → INCR (nicht GET + SET)
  • Objekt benötigt? → Hash (nicht serialisierter JSON-String)
  • Einmalige Elemente benötigt? → Set (nicht List + Dedup-Logik)
  • Ranking benötigt? → Sorted Set (nicht Sortierung nach Abfrage)

3. TTL auf alles setzen

Der Arbeitsspeicher ist begrenzt. Immer Ablaufzeiten für Cache-Keys setzen:

SET cache:api:result "{...}" EX 300   # 5-Minuten-TTL

4. Key-Benennungskonventionen verwenden

ressource:id:feld
user:1001:profile
cache:api:v2:products
session:abc123
queue:emails:pending

Persistenz: RDB vs. AOF

Redis kann Daten zur Datensicherheit auf die Festplatte schreiben:

Modus Funktionsweise Kompromiss
RDB Point-in-Time-Snapshots Schnelle Wiederherstellung, möglicher Datenverlust
AOF Protokolliert jeden Schreibvorgang Langsamer, minimaler Datenverlust
Beide RDB + AOF kombiniert Beste Datensicherheit
# In redis.conf
save 900 1          # Snapshot, wenn sich 1 Key in 900 s geändert hat
appendonly yes      # AOF aktivieren
appendfsync everysec  # Fsync jede Sekunde

Kurzreferenz für Befehle

Du möchtest einen bestimmten Redis-Befehl nachschlagen? Nutze unser Redis Cheat Sheet – es umfasst 200+ Befehle, nach Datenstruktur geordnet, mit Syntax, Beispielen und Einzel-Klick-Kopieren.

Fazit

Redis ist weit mehr als ein Cache. Seine Datenstrukturen passen perfekt zu echten Anwendungsanforderungen:

  • Strings → Sessions, Zähler, einfacher Cache
  • Hashes → Nutzerprofile, Konfigurationsobjekte
  • Lists → Queues, Aktivitäts-Feeds, zuletzt aufgerufene Elemente
  • Sets → Tags, eindeutige Besucher, Beziehungen
  • Sorted Sets → Bestenlisten, Rankings, Priority-Queues
  • Streams → Event-Sourcing, Message-Queues
  • Pub/Sub → Echtzeit-Benachrichtigungen, Chat

Starte mit Caching, aber hör nicht dort auf. Redis kann mehrere Infrastrukturkomponenten in deinem Stack ersetzen – und das alles mit Sub-Millisekunden-Geschwindigkeit.