CMD Guide
HomeSystem DesignCaching

Cache Replacement Policies

Cache Replacement Policies

A cache is a small, fast store that sits in front of a big, slow one (disk, a database, a remote service). It only earns its keep when the item you ask for is already inside it — a hit. When it isn't — a miss — you pay the full slow-path cost. The catch is that a cache is deliberately too small to hold everything: RAM costs money, and the working set is usually far larger than what you can afford to keep hot. So the moment the cache is full and a new item arrives, you must throw something out. The rule that decides which existing entry to evict is the cache replacement (or eviction) policy.

The whole game is prediction under uncertainty: you want to evict the entry you are least likely to need again soon, because every wrong eviction turns a future fast hit into a slow miss. No online policy can see the future, so each real policy is a cheap, computable proxy for "what will be reused." Understanding those proxies — and where each one is fooled — is the core skill an interviewer is testing.

How it works, precisely

Every policy answers one question when the cache is full: given the set of resident keys, which one leaves? The classic families:

Modern production caches blend these. LRU-K tracks the last K references to resist one-hit pollution. ARC (Adaptive Replacement Cache) keeps two LRU lists — one for recency, one for frequency — plus ghost lists of recently evicted keys, and shifts capacity between them based on which ghosts get hit. W-TinyLFU (used by Caffeine) fronts a small LRU "window" with an admission filter: a compact frequency sketch decides whether a newcomer is even worth displacing the current eviction victim.

ARC's "shifts capacity" deserves a concrete step, because the follow-up is always how much, decided by what? ARC splits the cache into T1 (recent — seen once) and T2 (frequent — seen at least twice), each shadowed by a ghost list (B1, B2) that remembers keys recently evicted from it, and keeps a target size p for T1. The adaptation rule: a hit in ghost B1 grows p (a recently-evicted one-timer came back — recency is being punished, give it more room); a hit in ghost B2 shrinks p (frequency is being punished). Trace it with cache size 4 and p = 2: T1 = {W, X}, T2 = {Y, Z}, ghost B1 = {A}. Access A → ghost hit in B1 → p increases to 3, and A is fetched and inserted into T2 (it has now been seen twice). The insertion forces an eviction, and the rule is: evict the LRU of T1 when |T1| > p, else the LRU of T2. Here |T1| = 2 ≤ p = 3, so the victim is T2's LRU end (Y) — which is precisely how the freed capacity shifts toward recency. Had T1 instead been over target (say T1 = {V, W, X} with p back at 2, so |T1| = 3 > p), the victim would have been T1's LRU end. ARC is a feedback controller: the ghost lists measure which kind of eviction you are regretting, and p moves toward whichever list is generating regret. (Mechanism per Megiddo & Modha, "ARC: A Self-Tuning, Low Overhead Replacement Cache", USENIX FAST 2003.)

A worked scenario

Say you run a product-catalog API at 50,000 QPS. A cache hit costs ~0.2 ms (in-memory), a miss costs ~8 ms (Postgres + row assembly). Traffic is Zipfian: the top 20% of SKUs drive ~80% of reads. Your cache holds 100,000 of 2,000,000 SKUs — 5% of the catalog.

Do the arithmetic on hit rate. At an 85% hit rate, average latency ≈ 0.85×0.2 + 0.15×8 = 1.37 ms, and the DB sees 50,000×0.15 = 7,500 QPS. Nudge the hit rate to 92% with a better policy and it's 0.92×0.2 + 0.08×8 = 0.82 ms with only 4,000 QPS hitting the DB — a 40% latency drop and nearly half the backend load, from the same memory budget. That delta is exactly what a good replacement policy buys you.

Here LRU does well because popular SKUs are re-touched constantly. But watch what a nightly analytics scan that reads all 2M SKUs once does to LRU: it streams cold keys through the cache, evicting your hot 100k in favor of items used exactly once. Hit rate collapses to near 5% until traffic re-warms it. LFU or W-TinyLFU shrug this off — the scanned keys never accumulate enough frequency to be admitted, so the hot set survives.

Trade-offs: when to use, when not

LRU — the sane default. Use it when access shows temporal locality (session data, recently viewed items, hot rows). It's cheap, O(1), and easy to reason about. Don't use plain LRU when large sequential scans pollute it, or when a truly popular-but-not-recent item keeps getting bumped by bursty newcomers.

LFU — use when popularity is stable over long windows (a CDN edge serving evergreen assets, dictionary/embedding lookups). Don't use naive LFU when popularity shifts: old hits give stale items an unearned high count, freezing them in ("cache ossification"). Mitigate with aged/decaying counters or windowed LFU.

FIFO — use only when metadata cost must be near zero and access is roughly uniform, or as a building block (e.g. FIFO-based S3-FIFO now rivals LRU with less overhead). Don't use it as a general reuse predictor — it ignores hits entirely.

Random — use in massively concurrent caches where LRU's per-access list mutation is a lock-contention bottleneck; eviction needs no global order. Don't use when you need predictable, explainable behavior.

ARC / W-TinyLFU — use when you want near-optimal hit rates that self-tune between recency and frequency and resist scan pollution — this is what Caffeine, RocksDB, and many databases actually ship. Don't reach for them when a plain LRU already saturates your hit rate; the extra sketches and ghost lists add memory and complexity you won't recoup. (Note: ARC's patent history pushed some projects toward LIRS/TinyLFU instead.)

Pitfalls an interviewer probes

Key takeaways

Drill ladder — surviving the follow-ups

L0 · A replacement policy evicts the entry least likely to be reused when the cache is full.

L1 · "Why does LRU fail on a full-table scan?"
Bar: The scan touches every key once, pushing hot items out. Use admission control (W-TinyLFU), segmented LRU, or scan-resistant policies.

L2 · "LFU sounds better than LRU — why isn't it the default?"
Bar: Old popular items keep high counts and never leave (cache ossification). Decaying counters or windowed LFU fix this.

L3 · "A hot key expires and 1,000 clients request it at once. What happens?"
Bar: Thundering herd — request coalescing / single-flight prevents 1,000 backend queries. stale-while-revalidate also helps.

L4 · "You move from one cache node to ten. Does LRU still work the same way?"
Bar: No — consistent hashing changes key distribution; hot keys can concentrate on one shard. Monitor per-shard hit ratio and add virtual nodes or re-shard.

L5 · "Design a cache for a feed that must be fresh within 30 seconds globally."
Bar: Use TTL ≤ 30 s + proactive invalidation; consider regional caches with pub/sub invalidation; measure tail freshness, not just average; avoid thundering herd on invalidation.

🤖 Don't fully get this? Learn it with Claude

Stuck on Cache Replacement Policies? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Cache Replacement Policies** (System Design) and want to truly understand it. Explain Cache Replacement Policies from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Cache Replacement Policies** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Cache Replacement Policies** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Cache Replacement Policies** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes