This episode covers how Redis is used in the real world as a cache and infrastructure: Cache-Aside, Write-Through, and Write-Behind strategies, eviction policies, a rate limiter with Sorted Sets, Redlock-style distributed locks, and the session store pattern.

The first three phases of this series equipped you with Redis commands and data structures. Episode 11 starts assembling them into real architectural patterns: how Redis is used as a cache, a request rate limiter, a distributed lock, and a session store.
This is the episode most asked about in backend interviews: when to use Cache-Aside, how to choose an eviction policy, and how a proper rate limiter works. You'll see how the data structures you've learned — Strings, Sorted Sets, Hashes — come together into solid patterns.
The most common and easiest-to-understand strategy. The application reads the cache first; on a miss, it reads the database, then writes the result to the cache:
GET data:1 → cache hit? → return
→ cache miss → query DB → SET data:1 value EX 300A short implementation in the application: check GET data:1, if nil it's a miss, fetch from the database, then SET data:1 value EX 300. The Cache-Aside advantage: simple, and the cache only holds data that's actually read. The weakness: the cache can go stale until the TTL expires — known as stale data.
The cache itself fetches data from the database on a miss (usually via a dedicated library). Application consumers only talk to the cache, not the database — but operating this layer is more complex.
| Strategy | Read | Write | Consistency | Complexity |
|---|---|---|---|---|
| Cache-Aside | DB on miss | Manual cache update | Can be temporarily stale | Low |
| Write-Through | DB on miss | Sync to cache | High | Medium |
| Write-Behind | DB on miss | Async to DB | Risk of data loss | High |
| Read-Through | Cache pulls from DB | Manual | Can be stale | Medium |
For most applications, Cache-Aside + TTL is the most sensible starting choice.
When maxmemory is reached, Redis must decide which keys to evict. This policy is set via maxmemory-policy:
redis-cli CONFIG GET maxmemory-policyredis-cli CONFIG GET maxmemory-policy shows the current policy. The options you need to know:
allkeys-lru: evict whichever key has been used least recently (approximate LRU) — the common choice for a pure cache.allkeys-lfu: evict the least frequently accessed keys — better for skewed access patterns.volatile-lru: evict the least-used keys that have a TTL; keys without a TTL are left alone.volatile-ttl: evict TTL keys that are closest to expiry.noeviction: evict nothing; write commands error with OOM command not allowed when used memory > 'maxmemory'.Info
If all your cache keys have a TTL, volatile-lru and allkeys-lru are equally effective. If permanent keys must be guaranteed to never disappear (e.g. reference data), use volatile-lru with TTLs only on cache keys.
For a pure cache with a hot access pattern, allkeys-lfu is often superior: frequently-read data survives, cold data gets evicted.
Limit requests per user with an accurate sliding window — the Sorted Set stores timestamps as scores, old elements are removed, then you count the number within the window:
redis-cli ZREMRANGEBYSCORE rl:user:42 -inf 1700000000
redis-cli ZADD rl:user:42 1700000000 "req-1"
redis-cli ZCARD rl:user:42This pattern removes requests older than the window (ZREMRANGEBYSCORE), adds the new request (ZADD), then checks ZCARD to see whether the limit has been exceeded. For full accuracy without race conditions, wrap this logic in a Lua script (episode 10) — that's why production rate limiters always use Lua.
When several application instances must ensure only one is working (e.g. a single cron scheduler), use a distributed lock. Based on SET NX PX:
redis-cli SET lock:job "instance-1" NX PX 30000
redis-cli EVAL "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end" 1 lock:job "instance-1"The first line acquires the lock with SET ... NX PX 30000 — it only succeeds if the key doesn't exist yet, and it's valid for 30 seconds. The second line releases the lock only if the owner is correct (via Lua) — preventing deletion of another instance's lock. Redlock extends this pattern to several Redis nodes for higher tolerance, at the cost of added complexity.
A pattern you already saw in episode 4, summarized here because it's a core web application pattern:
redis-cli HSET session:9f3a userId 123 role "admin"
redis-cli EXPIRE session:9f3a 1800HSET session:9f3a userId 123 role "admin" stores the session as an object, EXPIRE gives it a 30-minute lifetime. Expired sessions are cleaned up automatically by Redis — the application needs no cron cleanup. This pattern is used by almost every web framework with a Redis session store enabled.
Episode 11 assembled Redis data structures into production patterns: Cache-Aside, Write-Through, Write-Behind, and Read-Through strategies; eviction policies like allkeys-lru and allkeys-lfu; a Sorted Set-based rate limiter; Redlock distributed locking; and a Hash + TTL session store.
Key takeaways:
allkeys-lfu excels for hot access patterns; noeviction makes writes error when full.SET key value NX PX; release must validate the owner via Lua.In the next episode, episode 12, we start the high availability phase: Replication (Master-Replica) & Redis Sentinel. You'll learn asynchronous replication with REPLICAOF, read scaling, then Redis Sentinel as a watchdog with automatic failover. Let's continue!