What it is
A cache keeps a copy of hot data somewhere faster than its source of truth — usually memory (Redis, Memcached) in front of a database. It works because access patterns are brutally skewed: the 80/20 rule understates it; for most consumer systems 5% of keys take 95% of reads.
In an interview, a cache is rarely optional. Any read-heavy design (feeds, redirects, product pages) is expected to have one, and the scoring is on whether you can name the strategy, the eviction policy, the invalidation story, and the failure modes — not on whether you drew the Redis box.
I'll put Redis in front of Postgres, cache-aside with a one-day TTL. The interesting problems are invalidation and what happens when the cache is cold, so let me cover both.
The picture
The variants and when to pick each
Cache-aside — the default
The application checks the cache first; on a miss it reads the database and writes the result back with a TTL. The cache never talks to the database and holds only data that was actually requested.
This is the answer in nine interviews out of ten. It tolerates cache node loss (it's just a miss storm), and stale data is bounded by the TTL.
Write-through and write-back
Write-through writes to cache and database synchronously — reads after writes are always fresh, at the cost of write latency. Write-back acknowledges the write once it's in the cache and flushes to the database asynchronously — spectacular write throughput, used inside databases and hardware, and dangerous as an application pattern because a crashed cache node loses acknowledged writes.
Name write-back to show range, then decline it: durable data belongs in the database first.
Eviction: LRU is the default, and know why
Memory is finite, so something must go. LRU (least recently used) matches skewed access patterns and is O(1) with the classic hashmap + doubly-linked-list construction — a favourite adjacent coding question. LFU resists one-off scans polluting the cache but adapts slowly when the hot set shifts. Redis actually ships approximate LRU/LFU via sampling, which is worth saying — precision costs memory and the approximation loses almost nothing.
The three failure modes interviewers actually probe
Stampede (thundering herd): a hot key expires and ten thousand requests hit the database simultaneously. Fixes: per-key mutex so one request rebuilds while others wait, probabilistic early refresh, or background refresh for known-hot keys.
Penetration: requests for keys that don't exist bypass the cache every time. Fixes: cache negative results with a short TTL, or a Bloom filter in front of the database.
Avalanche: many keys expire at once (deploy-time mass-set with identical TTLs) or a cache node dies cold. Fixes: jitter the TTLs, warm caches before traffic, replicate hot shards.
Numbers worth memorising
| Memory read (Redis, same DC) | ~100 µs round trip, ~1 ms p99 |
| Postgres point read (indexed, warm) | ~1–10 ms |
| Redis throughput per node | ~100k ops/s |
| Hit-rate target for a read-heavy path | >90% (feeds and redirects reach 99%) |
| Cache sizing rule | 20% of hot data ≈ 80%+ of reads |
Where the interviewer pushes
The push is almost always invalidation: "the user updates their profile — when do readers see it?" The strong answer names the write path explicitly: update the database, then delete (don't update) the cache key, and accept the small race where a concurrent reader re-caches stale data inside the TTL window. Updating instead of deleting turns a race into permanent staleness under concurrent writes.
Second push: "Redis dies — what happens?" Walk the brown-out: traffic fails over to the database, latency degrades 10–100×, and the database must survive the miss storm — which is why you rate-limit the backfill and keep replicas for read capacity. If the database can't survive a cold cache, the cache isn't an optimisation any more, it's a load-bearing wall — call that out; it changes the availability math of the whole design.