Step 4
The five roles of Redis
0 views
The five roles of Redis
Redis is often asked "what is it?" for good reason. It plays five distinct parts.
1. Cache
async function getUser(id: number) {
const cached = await redis.get(`user:${id}`);
if (cached) return JSON.parse(cached);
const u = await db.query("SELECT * FROM users WHERE id=$1", [id]);
await redis.setex(`user:${id}`, 300, JSON.stringify(u));
return u;
}
TTL is mandatory.
2. Sessions
const sid = crypto.randomUUID();
await redis.setex(`sess:${sid}`, 3600, JSON.stringify({ userId, role }));
res.cookies.set("session", sid, { httpOnly: true, maxAge: 3600 });
Easy invalidation via DEL; every request hits Redis.
3. Rate limit
const bucket = Math.floor(Date.now() / 60000);
const k = `rl:${ip}:${bucket}`;
const count = await redis.incr(k);
if (count === 1) await redis.expire(k, 120);
if (count > 60) return 429;
4. Pub/Sub
redis.publish("notifications", JSON.stringify({ userId, message }));
const sub = redis.duplicate();
await sub.subscribe("notifications");
sub.on("message", (_, raw) => { /* broadcast via WS */ });
Fan-out events to multiple app instances.
5. Distributed lock
async function withLock(key: string, ttl: number, fn: () => Promise<void>) {
const acquired = await redis.set(`lock:${key}`, "1", "EX", ttl, "NX");
if (!acquired) throw new Error("locked");
try { await fn(); } finally { await redis.del(`lock:${key}`); }
}
6. Separating instances (optional)
redis-cache— lossy OKredis-session— losing logs users outredis-queue— job queue
Start with one for MVP.
7. Data structures
| Type | Use |
|---|---|
| String | cache, counters |
| Hash | field-level object updates |
| List | queue, recent-N |
| Set | unique values (likes, follows) |
| Sorted Set | rankings, timelines |
| Stream | event log (light Kafka alternative) |
8. Memory
maxmemory 2gb
maxmemory-policy allkeys-lru
For cache, allkeys-lru evicts the least recently used.
9. Gotchas
- Missing TTL → memory growth
- Large values → network/memory waste. Put big blobs in S3.
- No pool → per-request connect is slow (ioredis handles pool)
- Pub/Sub treated as persistent queue → messages dropped
10. When you don't need Redis
- Small traffic, DB is fast enough
- Single server (no locks / pub-sub required)
- Next.js
unstable_cache+ ISR replaces cache
Add Redis when measured bottlenecks demand it.
Closing
Treating Redis as "a fast DB" wastes memory. Pick the role and match the data structure.
Next
- 05-three-layer-cache