Redis mit Claude Code: Pub/Sub, Caching & Real-Time 2026

Redis ist mehr als ein Cache — es ist ein vielseitiges In-Memory-System fuer Pub/Sub-Messaging, Job Queues, Rate Limiting und Event Streaming. Claude Code kennt alle Redis-Datenstrukturen und waehlt automatisch die richtige fuer jeden Use Case.

Warum Redis + Claude Code eine natuerliche Kombination ist

Redis hat ueber 20 Datenstrukturen mit jeweils spezifischen Staerken. Die richtige zu waehlen bedeutet oft den Unterschied zwischen O(1) und O(n). Claude Code kennt diese Tradeoffs — und erklaert sie beim Implementieren. So entsteht kein Cargo-Cult-Code, sondern durchdachte Redis-Architekturen die in Production standhalten.

Claude Code Workflow fuer Redis: Beschreibe das Problem in natuerlicher Sprache — "Ich brauche Rate Limiting fuer meine API mit 100 Requests pro Minute pro User" — Claude Code waehlt die passende Datenstruktur (hier: Sorted Set mit Sliding Window) und implementiert sie vollstaendig mit ioredis, inklusive Monitoring-Hooks.

1. Redis Grundlagen: Die richtigen Datenstrukturen waehlen

Jede Redis-Datenstruktur hat einen optimalen Einsatzbereich. Claude Code waehlt anhand des Use Case — nie einfach "String fuer alles". Das ist der Unterschied zwischen einem Redis-Anfaenger und einem Redis-Experten.

DatenstrukturUse CasesTypische OperationenKomplexitaet
StringCounter, Caching, Flags, JSON-BlobsGET, SET, INCR, SETNXO(1)
HashUser-Objekte, Konfiguration, SessionsHGET, HSET, HMGET, HGETALLO(1)/O(n)
ListMessage Queues, Activity Feeds, LogsLPUSH, RPOP, LRANGE, BLPOPO(1)/O(n)
SetTags, Unique Visitors, BeziehungenSADD, SISMEMBER, SUNIONO(1)/O(n)
Sorted SetLeaderboards, Rate Limiting, ZeitreihenZADD, ZRANGE, ZRANGEBYSCOREO(log n)
StreamEvent Sourcing, Audit Log, IoT-DatenXADD, XREAD, XGROUPO(log n)/O(n)
BitmapDaily Active Users, Feature FlagsSETBIT, GETBIT, BITCOUNTO(1)/O(n)

Core Datenstrukturen richtig nutzen

// Claude Code Prompt: "Zeig mir alle Redis-Datenstrukturen // mit konkreten Business Use Cases in Node.js" const Redis = require('ioredis'); const redis = new Redis({ host: 'localhost', port: 6379 }); // STRING: Caching + atomarer Counter await redis.set('user:42:profile', JSON.stringify(user), 'EX', 3600); await redis.incr('stats:pageviews'); const cached = await redis.get('user:42:profile'); // HASH: User-Profil (effizienter als JSON-String fuer partielle Updates) await redis.hset('user:42', { name: 'Alice', email: 'alice@example.com', plan: 'pro', loginCount: 0 }); await redis.hincrby('user:42', 'loginCount', 1); const plan = await redis.hget('user:42', 'plan'); // LIST: FIFO Queue fuer Background Jobs await redis.lpush('queue:emails', JSON.stringify({ to: 'alice@example.com', template: 'welcome' })); const job = await redis.brpop('queue:emails', 5); // SET: Unique Tags pro Artikel (O(1) Membership-Check) await redis.sadd('article:99:tags', 'redis', 'caching', 'performance'); const hasTag = await redis.sismember('article:99:tags', 'redis'); // SORTED SET: Leaderboard mit Scores await redis.zadd('leaderboard:2026', 1850, 'alice'); await redis.zadd('leaderboard:2026', 2100, 'bob'); const top3 = await redis.zrevrange('leaderboard:2026', 0, 2, 'WITHSCORES');
Wann kein Redis: Redis ist kein Ersatz fuer eine relationale Datenbank. Kein ACID ohne Redis Transactions (MULTI/EXEC), kein komplexes Querying, kein JOIN. Claude Code weist explizit darauf hin und empfiehlt PostgreSQL fuer persistente Hauptdaten.

2. Caching-Strategien: Cache-Aside, Write-Through und TTL-Design

Falsches Caching ist schlimmer als kein Caching: Stale Data, Cache Stampedes, Memory-Ueberlaeufe. Claude Code implementiert die richtige Strategie fuer jeden Use Case und erklaert die Tradeoffs.

Cache-Aside (Lazy Loading) App prueft Cache → Miss → DB → in Cache schreiben. Ideal fuer read-heavy Workloads. Cache enthaelt nur tatsaechlich gelesene Daten — kein unnoetig belegter Speicher.
Write-Through Jeder Write geht gleichzeitig in Cache + DB. Cache ist immer aktuell. Overhead beim Schreiben, kein Cache Miss beim ersten Read.
Write-Behind (Write-Back) Write in Cache sofort, DB async. Maximal performant, aber Datenverlustrisiko bei Crash. Nur fuer unkritische Daten verwenden.
Read-Through Cache-Schicht sitzt transparent vor DB. App kennt keinen Cache. Einfach, aber weniger Kontrolle ueber Cache-Logik und Invalidierung.

Cache Cache-Aside mit Cache Stampede Prevention

// Prompt: "Implementiere Cache-Aside mit Probabilistic Early Recomputation // (verhindert Cache Stampede) und TTL-Jitter" class CacheService { constructor(redis, db) { this.redis = redis; this.db = db; this.DEFAULT_TTL = 3600; this.BETA = 1; } // TTL-Jitter: verhindert synchronized Expiry (Thundering Herd) jitterTTL(baseTTL) { const jitter = Math.floor(Math.random() * baseTTL * 0.1); return baseTTL + jitter; } async get(key, fetchFn, ttl = this.DEFAULT_TTL) { const cached = await this.redis.get(key); if (cached) { const { value, delta } = JSON.parse(cached); const remainingTTL = await this.redis.ttl(key); const now = Date.now() / 1000; // PER: mit Wahrscheinlichkeit fruehzeitig neu berechnen const shouldRecompute = now - delta * this.BETA * Math.log(Math.random()) >= remainingTTL; if (!shouldRecompute) return value; } const start = Date.now(); const value = await fetchFn(); const delta = (Date.now() - start) / 1000; await this.redis.set( key, JSON.stringify({ value, delta }), 'EX', this.jitterTTL(ttl) ); return value; } // Cache Invalidation mit SCAN (niemals KEYS in Production!) async invalidate(pattern) { const keys = []; let cursor = '0'; do { const [nextCursor, found] = await this.redis.scan( cursor, 'MATCH', pattern, 'COUNT', 100 ); cursor = nextCursor; keys.push(...found); } while (cursor !== '0'); if (keys.length > 0) await this.redis.del(...keys); return keys.length; } } // Nutzung const cache = new CacheService(redis, db); const user = await cache.get( `user:${userId}`, () => db.query('SELECT * FROM users WHERE id = $1', [userId]), 1800 ); // User-Cache invalidieren nach Update await cache.invalidate(`user:${userId}*`);

3. Pub/Sub fuer Real-Time: PUBLISH, SUBSCRIBE und horizontales Scaling

Redis Pub/Sub ermoeglicht echtes Real-Time Messaging zwischen Services. Mit dem Socket.io Redis Adapter koennen WebSocket-Apps ueber mehrere Server-Instanzen horizontal skalieren — ohne Sticky Sessions oder komplexe Load-Balancer-Konfiguration.

Pub/Sub Multi-Channel Pub/Sub mit Pattern-Subscriptions

// Prompt: "Pub/Sub mit Pattern-Subscriptions, Auto-Reconnect, // JSON-Parsing und typsicherem Event-System" const Redis = require('ioredis'); // WICHTIG: Pub und Sub brauchen SEPARATE Redis-Verbindungen! const publisher = new Redis({ host: 'localhost', port: 6379 }); const subscriber = new Redis({ host: 'localhost', port: 6379 }); class EventPublisher { async publish(channel, event, data) { const message = JSON.stringify({ event, data, timestamp: Date.now(), id: crypto.randomUUID() }); const receivers = await publisher.publish(channel, message); return receivers; } // Domain-spezifische Publisher-Methoden async userCreated(userId, data) { return this.publish('users:events', 'user.created', { userId, ...data }); } async orderUpdated(orderId, status) { return this.publish(`orders:${orderId}`, 'order.status_changed', { orderId, status }); } } class EventSubscriber { constructor() { this.handlers = new Map(); subscriber.on('pmessage', (pattern, channel, raw) => { try { const msg = JSON.parse(raw); const handler = this.handlers.get(pattern); if (handler) handler(channel, msg); } catch (err) { console.error(`Parse error on ${channel}:`, err.message); } }); subscriber.on('reconnecting', () => { console.log('Redis Subscriber reconnecting...'); }); } async subscribe(pattern, handler) { this.handlers.set(pattern, handler); await subscriber.psubscribe(pattern); } } // Socket.io + Redis Adapter fuer horizontales Scaling const { createAdapter } = require('@socket.io/redis-adapter'); const pubClient = new Redis({ host: 'localhost', port: 6379 }); const subClient = pubClient.duplicate(); io.adapter(createAdapter(pubClient, subClient)); // Jetzt funktioniert emit() ueber ALLE Server-Instanzen: io.emit('notification', { msg: 'Neue Nachricht' }); io.to('room:premium').emit('update', data);
Pub/Sub Einschraenkung: Redis Pub/Sub ist Fire-and-Forget — kein Persistence, kein ACK, kein Replay. Wenn ein Subscriber offline ist, verliert er die Nachricht. Fuer persistente Events Redis Streams verwenden (Abschnitt 6).

4. Rate Limiting: Sliding Window und Token Bucket mit Lua Scripts

Rate Limiting schuetzt APIs vor Abuse und sichert faire Ressourcenverteilung. Claude Code implementiert beide Algorithmen atomisch in Redis — ohne Race Conditions, auch unter hoher Last.

Rate Limit Sliding Window mit Sorted Sets

// Prompt: "Sliding Window Rate Limiter: 100 Requests/60s pro User, // atomare Pipeline, korrekte Headers" class SlidingWindowRateLimiter { constructor(redis, { limit = 100, windowSeconds = 60 } = {}) { this.redis = redis; this.limit = limit; this.windowMs = windowSeconds * 1000; } async isAllowed(identifier) { const key = `ratelimit:${identifier}`; const now = Date.now(); const windowStart = now - this.windowMs; // Atomare Pipeline: alle Ops in einem Round-Trip const pipeline = this.redis.pipeline(); pipeline.zremrangebyscore(key, 0, windowStart); pipeline.zadd(key, now, `${now}-${Math.random()}`); pipeline.zcard(key); pipeline.pexpire(key, this.windowMs); const results = await pipeline.exec(); const count = results[2][1]; return { allowed: count <= this.limit, remaining: Math.max(0, this.limit - count), resetAt: now + this.windowMs, current: count }; } } // Token Bucket via Lua Script (atomarer Refill) const TOKEN_BUCKET_SCRIPT = ` local key = KEYS[1] local capacity = tonumber(ARGV[1]) local refillRate = tonumber(ARGV[2]) local requested = tonumber(ARGV[3]) local now = tonumber(ARGV[4]) local data = redis.call('HMGET', key, 'tokens', 'lastRefill') local tokens = tonumber(data[1]) or capacity local lastRefill = tonumber(data[2]) or now local elapsed = (now - lastRefill) / 1000 local newTokens = math.min(capacity, tokens + elapsed * refillRate) if newTokens >= requested then redis.call('HMSET', key, 'tokens', newTokens - requested, 'lastRefill', now) redis.call('PEXPIRE', key, 60000) return 1 else redis.call('HMSET', key, 'tokens', newTokens, 'lastRefill', now) redis.call('PEXPIRE', key, 60000) return 0 end`; // Express Middleware function rateLimitMiddleware(limiter) { return async (req, res, next) => { const id = req.user?.id || req.ip; const { allowed, remaining, resetAt } = await limiter.isAllowed(id); res.set({ 'X-RateLimit-Limit': limiter.limit, 'X-RateLimit-Remaining': remaining, 'X-RateLimit-Reset': Math.ceil(resetAt / 1000) }); if (!allowed) { return res.status(429).json({ error: 'Too Many Requests', retryAfter: Math.ceil((resetAt - Date.now()) / 1000) }); } next(); }; }

5. BullMQ Job Queue: Zuverlassige Background Jobs mit Redis

BullMQ ist die Standard-Wahl fuer Redis-basierte Job Queues in Node.js. Claude Code implementiert Queues mit Exponential Backoff, Concurrency-Control, Dead Letter Queues und Monitoring-Hooks.

Queue BullMQ mit Retry, Concurrency und Events

// Prompt: "BullMQ fuer Email + PDF Queue mit: // - Exponential Backoff, Job-Progress, Graceful Shutdown" const { Queue, Worker, QueueEvents } = require('bullmq'); const connection = { host: 'localhost', port: 6379 }; const emailQueue = new Queue('emails', { connection }); const pdfQueue = new Queue('pdf', { connection }); // Jobs hinzufuegen mit vollstaendigen Optionen async function queueEmail(to, template, data) { return emailQueue.add('send', { to, template, data }, { attempts: 3, backoff: { type: 'exponential', delay: 2000 }, removeOnComplete: { count: 100 }, removeOnFail: { count: 500 }, priority: 1 }); } // Delayed Job: 5 Minuten nach Event await pdfQueue.add('invoice', { orderId: 42 }, { delay: 5 * 60 * 1000, attempts: 5 }); // Worker mit Concurrency + Progress-Tracking const emailWorker = new Worker('emails', async (job) => { const { to, template, data } = job.data; await job.updateProgress(10); const html = await renderTemplate(template, data); await job.updateProgress(50); await sendgrid.send({ to, html }); await job.updateProgress(100); return { sent: true, ts: Date.now() }; }, { connection, concurrency: 5 }); // Events fuer Monitoring const events = new QueueEvents('emails', { connection }); events.on('completed', ({ jobId }) => console.log(`Done: ${jobId}`)); events.on('failed', ({ jobId, failedReason }) => { console.error(`FAIL ${jobId}: ${failedReason}`); // Alert an Monitoring senden }); // Graceful Shutdown (Pflicht in Production) process.on('SIGTERM', async () => { await emailWorker.close(); process.exit(0); });
Bull vs. BullMQ: BullMQ (v4+) ist der offizielle Nachfolger von Bull. Es nutzt Redis Streams intern statt Listen und unterstuetzt Worker Threads. Fuer neue Projekte immer BullMQ waehlen. Claude Code kennt beide APIs und migriert bestehenden Bull-Code auf Anfrage.

6. Redis Streams: Consumer Groups, Backpressure und Event Sourcing

Redis Streams sind fuer persistente, reihenfolgetreue Event-Logs optimiert — aehnlich wie Apache Kafka, aber direkt in Redis eingebettet. Consumer Groups ermoglichen Load Balancing zwischen Worker-Instanzen mit Acknowledgement-Semantik und At-Least-Once Delivery.

Stream Consumer Groups mit ACK und Pending-Handling

// Prompt: "Redis Streams Consumer Group fuer Order-Events // mit ACK, Pending-Message-Recovery und Backpressure" class StreamProducer { constructor(redis, streamName) { this.redis = redis; this.stream = streamName; } async publish(fields) { // XADD: ID '*' = auto-generierte ID (Timestamp-basiert) const id = await this.redis.xadd(this.stream, '*', ...Object.entries(fields).flat()); return id; } // Stream-Groesse begrenzen (MAXLEN) async publishBounded(fields, maxLen = 10000) { return this.redis.xadd(this.stream, 'MAXLEN', '~', maxLen, '*', ...Object.entries(fields).flat() ); } } class StreamConsumer { constructor(redis, streamName, groupName, consumerName) { this.redis = redis; this.stream = streamName; this.group = groupName; this.consumer = consumerName; } async init() { try { // Group erstellen ($ = nur neue Messages ab jetzt) await this.redis.xgroup('CREATE', this.stream, this.group, '$', 'MKSTREAM'); } catch (err) { if (!err.message.includes('BUSYGROUP')) throw err; } } async consume(handler, { count = 10, blockMs = 2000 } = {}) { while (true) { // XREADGROUP: '>' = nur unbestaetigt neue Messages const results = await this.redis.xreadgroup( 'GROUP', this.group, this.consumer, 'COUNT', count, 'BLOCK', blockMs, 'STREAMS', this.stream, '>' ); if (!results) continue; for (const [stream, messages] of results) { for (const [id, fields] of messages) { try { const data = this.parseFields(fields); await handler(data, id); // ACK: Message als verarbeitet markieren await this.redis.xack(this.stream, this.group, id); } catch (err) { console.error(`Failed to process ${id}:`, err.message); // Kein ACK = Message bleibt im Pending State } } } } } // Pending Messages recovern (z.B. nach Consumer-Crash) async recoverPending(idleMs = 30000) { const pending = await this.redis.xpending( this.stream, this.group, '-', '+', 100 ); for (const msg of pending) { const [id, consumer, idle] = msg; if (idle > idleMs) { // XCLAIM: Message an diesen Consumer uebergeben await this.redis.xclaim( this.stream, this.group, this.consumer, idleMs, id ); } } } parseFields(fields) { const obj = {}; for (let i = 0; i < fields.length; i += 2) { obj[fields[i]] = fields[i + 1]; } return obj; } } // Event Sourcing Pattern mit Redis Streams const producer = new StreamProducer(redis, 'orders:events'); const consumer = new StreamConsumer(redis, 'orders:events', 'fulfillment', 'worker-1'); await producer.publishBounded({ type: 'order.placed', orderId: 'ord-123', userId: 'usr-42', amount: '99.90' }); await consumer.init(); await consumer.consume(async (event, id) => { console.log(`Processing order: ${event.orderId}`); await fulfillOrder(event); });
Pub/Sub vs. Streams: Pub/Sub ist Fire-and-Forget — verpasste Messages sind weg. Streams persistieren Messages und erlauben Consumer Groups mit ACK-Semantik. Fuer kritische Events (Orders, Payments) immer Streams nutzen.

Redis Best Practices: Was Claude Code automatisch anwendet

Key-Naming Convention Schema: domain:entity:id:field — z.B. user:42:sessions. Konsistente Namespaces vermeiden Kollisionen und erleichtern Pattern-basierte Invalidierung.
Connection Pooling mit ioredis ioredis handhabt Connection Pooling automatisch. Fuer Pub/Sub und normale Ops immer separate Verbindungen nutzen — Subscriber-Verbindungen koennen keine anderen Befehle senden.
Memory-Limits setzen maxmemory 512mb + maxmemory-policy allkeys-lru in redis.conf. Ohne Limit frisst Redis beliebig viel RAM. Claude Code konfiguriert Memory-Policies passend zum Use Case.
Lua Scripts fuer Atomizitaet Mehrere Redis-Ops atomar: EVAL mit Lua Script. Kein Risiko fuer Race Conditions zwischen WATCH/MULTI/EXEC. Claude Code nutzt Lua fuer Rate Limiting und komplexe Zustands-Updates.
Pipeline fuer Batch-Ops Statt 10 einzelne Requests: redis.pipeline() buendelt alle Befehle in einem Round-Trip. Deutlich schneller bei vielen kleinen Operationen.
SCAN statt KEYS KEYS *pattern* blockiert Redis-Event-Loop. SCAN mit COUNT und Cursor ist non-blocking. Claude Code verwendet grundsaetzlich SCAN in Production-Code.

Session Redis Session Store fuer Express

// Prompt: "Express Session Store mit Redis: // secure cookies, HTTPOnly, CSRF-Schutz, 7-Tage TTL" const session = require('express-session'); const RedisStore = require('connect-redis').default; const redis = new Redis({ host: 'localhost', port: 6379 }); app.use(session({ store: new RedisStore({ client: redis, prefix: 'sess:' }), secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false, cookie: { secure: process.env.NODE_ENV === 'production', httpOnly: true, sameSite: 'strict', maxAge: 7 * 24 * 60 * 60 * 1000 } })); // Session-Daten lesen und schreiben app.post('/login', async (req, res) => { const user = await authenticate(req.body.email, req.body.password); req.session.userId = user.id; req.session.plan = user.plan; res.json({ success: true }); }); // Logout: Session aus Redis loeschen app.post('/logout', (req, res) => { req.session.destroy((err) => { if (err) return res.status(500).json({ error: err }); res.clearCookie('connect.sid'); res.json({ success: true }); }); });

Redis-Modul im Claude Code Kurs

Im Claude Code Mastery Kurs gibt es ein vollstaendiges Redis-Modul: alle Datenstrukturen, Caching-Strategien, Pub/Sub, BullMQ Queues, Streams, Rate Limiting und Production-Konfiguration — mit getesteten Code-Templates die sofort einsetzbar sind.

14 Tage kostenlos testen →