Design a Distributed Cache
mediumA cache holds recently used data in memory so you do not have to fetch it from a slower database. A distributed cache spreads that memory across many machines, because one machine does not have enough.
Key Concepts
item:99#1 through item:99#10 — and have clients pick one
at random, which spreads the load across ten machines at ten times the memory for that key. Or let each
application server keep its own copy in memory for a few seconds, so most requests never leave the process.
Both accept slightly stale data in exchange for surviving.High-level design
Client → (proxy?) → consistent-hash route → shard.
Per-shard: in-memory store (Memcached / Redis).
Optional per-shard replica for HA.
Client SDK handles retry, failover, batch fetch.
Hot-key detector emits alerts; load balancer for replicated hot keys.
Cold-start: shadow traffic from old cluster or replay from persistent log.
Components
- Client library with consistent hashing + retry.
- Cache nodes (Memcached, Redis).
- Optional proxy layer (mcrouter, twemproxy, KeyDB).
- Hot-key detector (top-K with Misra-Gries or count-min sketch).
- Metrics: hit rate, evictions, memory, latency, hot-key alarms, slow-log.
- Capacity planner: sizing by working-set estimate.
Miss storm on node loss, worked
Cache nodes 10 Total traffic 100,000 req/s Hit rate 95% -------------------------------------------------------------- Normal database load 100,000 x 0.05 = 5,000 req/s
One node lost (consistent hashing, no replication)
Share of keys now missing 1/10 = 10%
Extra misses 100,000 x 0.10 = 10,000 req/s
Database load 5,000 + 10,000 = 15,000 req/s
= 3x normal Same failure WITHOUT consistent hashing (modulo)
Keys remapped = ~90%
Extra misses = 90,000 req/s
Database load = 19x normal
-- an outage. Recovery
Misses repopulate the cache, so the spike decays over the time
it takes to touch the hot keys again -- typically seconds to
minutes. The question is whether the database survives the peak,
not the average. Mitigations, in order of cost
1. Consistent hashing (already assumed)
2. Request coalescing so 10,000 misses on the same key become 1
3. Serve-stale during repopulation
4. Replication of cache entries (doubles memory)Hot key handling
The problem
Key placement is per key. A key taking 30% of 100,000 req/s is
30,000 req/s on ONE node, whatever the ring looks like. Fix 1 -- suffixed replicas
Store as key#1 .. key#10, client picks one at random.
Effect: 30,000 req/s spread across 10 nodes.
Cost: 10x memory for that key; invalidation must touch all
copies, so staleness is bounded by how fast you can fan out. Fix 2 -- client-local cache
Cache the hot key in the application process for 1-5 seconds.
Effect: nearly all reads never leave the process.
Cost: staleness up to the local TTL, and each process holds its
own copy. Fix 3 -- detect and promote automatically
Track per-key request rates at the client; when a key crosses a
threshold, switch it to local caching for a window.
Cost: complexity; but it handles keys that become hot without a
human noticing. What does NOT work
Adding nodes. The key still lives on one of them.Failure modes in production
Topology disagreement between clients
Symptom: hit rate halves after a membership change.
Cause: clients hold different ring views; the same key routes to
two nodes.
Fix: version the ring, distribute through one control plane, and
accept that a brief disagreement costs hit rate, not correctness.
Cache used as a database
Symptom: a restart loses data nobody expected to lose.
Cause: values written only to the cache.
Fix: be explicit about reconstructibility; if it cannot be
rebuilt, it needs a durable home. Eviction thrash
Symptom: hit rate collapses while memory is full; CPU high.
Cause: working set exceeds capacity; every insert evicts
something that is about to be needed.
Fix: measure the working set and size for it, or admit only keys
seen more than once (admission policy) so one-off scans do not
evict the head. Connection storm on failover
Symptom: the replacement node is overwhelmed as it comes up.
Cause: every client reconnects and repopulates at once.
Fix: jittered reconnect, slow-start on the new node, and request
coalescing so concurrent misses for one key become one origin
read.