How does multi-level caching work — in-process L1 + Redis L2 + CDN — and why layer them?
Stacking an in-process cache, a shared Redis layer, and a CDN turns one hit-rate problem into three, each catching what the last one missed.
LEARN BY DOINGPut your intuition to work3 steps
How does multi-level caching work — in-process L1 + Redis L2 + CDN — and why layer them?
Use the lesson’s examples and explanation to check your idea.
Personal reflection. Nothing is sent to an AI service.#The shape of the problem: latency, capacity, and blast radius all trade off
Every cache sits somewhere on a spectrum: closer to the code that needs the data (fast, tiny, private) or closer to the rest of the world (slower, huge, shared). No single layer is good at both ends of that spectrum at once, which is why production systems that actually care about latency stack several of them rather than picking one.
- L1 — in-process cache. A plain in-memory map (or a library like Caffeine on the JVM, an LRU dict in a Node/Python process) living inside the same process serving the request. No network hop at all — reads cost nanoseconds to low microseconds. But it's private to one instance, capped by that instance's memory, and evaporates on every restart or deploy.
- L2 — shared cache (Redis/Memcached). One logical store all instances talk to over the network. Slower than L1 (a round-trip, typically sub-millisecond to a few milliseconds), but consistent across every instance and far larger.
- CDN — edge cache. Caches whole HTTP responses at points of presence close to the end user, geographically distributed. Great for anything cacheable and not personalized; useless for authenticated, per-user data, but able to serve a request without your infrastructure being involved at all.
- Origin. The database or the expensive computation that produces the value in the first place, when every layer above has missed.
#How a request actually moves through the stack
A concrete case where all three layers legitimately sit in this order: a pricing service that reads reference data (currency rates, feature flags, translated marketing copy) published by an upstream content system that itself sits behind a CDN. The service checks its own L1 first, falls back to the shared Redis L2 that every instance of that service populates, and only on a miss there does it make a real network call — which lands on the CDN edge in front of the publishing system before ever reaching that system's own database.
Every miss you avoid at L1 is a miss avoided at every layer behind it. That compounding is the actual reason to layer caches instead of just picking "the best" one: if L1 catches 80% of requests, L2 only ever sees the remaining 20% — and if L2 catches 80% of that, the CDN and origin see just 4% of the original traffic. Each layer only needs to be decent at catching what got past the layer in front of it.
#Freshness gets harder to reason about, not easier
The cost of this compounding hit rate is compounding staleness risk. A value can be simultaneously "fresh" in the database, invalidated in L2 by the write path described in a companion lesson on multi-instance consistency, and still stale in a dozen different L1s until each one's TTL expires or a pub/sub message reaches it. As a rule of thumb, TTLs should get longer the further a layer sits from the write path and the harder it is to invalidate directly:
| Layer | Typical TTL | Why |
|---|---|---|
| L1 (in-process) | Seconds to tens of seconds | Cheapest to be wrong about briefly; hardest to invalidate remotely (needs pub/sub) |
| L2 (Redis) | Minutes | Can be invalidated directly by any writer; shared, so one delete fixes it for everyone |
| CDN | Minutes to hours, or "forever" with explicit purge | Purging is an explicit API call, often to a third party; versioned URLs sidestep the problem entirely |
🧠 Check your understanding
0/2 · 0/2 answered1. Why do multi-level caches typically use a shorter TTL on L1 (in-process) than on L2 (shared Redis)?
2. What's the main reason layering L1, L2, and a CDN reduces load on the origin database more than any single cache alone?