Types of Caching
Every cache works the same way: it keeps a copy of an expensive-to-produce answer physically closer to whoever asks for it, so the next asker is served from the copy instead of re-doing the work. The only thing that changes between "types" is where that copy sits on the path from the user's eyeball to the origin data — and which medium holds the bytes.
That is the key to un-muddling the usual list. "In-memory", "disk", "database", "client-side", "server-side", "CDN", and "DNS" are not seven parallel things. They are points on two independent axes:
- Location axis — how far along the request path the cache lives: browser → DNS resolver → CDN edge → app server → database → disk.
- Medium axis — what physically stores the bytes: RAM (in-memory) or disk.
A Redis cache is both server-side (location) and in-memory (medium). A CDN edge holds objects in RAM and on disk. So "in-memory caching" is not an alternative to "server-side caching" — it describes a different question. Read the classic list as: pick a spot on the path, pick a medium.
The classic seven, placed on the two axes
Here is the same list you'll see everywhere, but sorted so the overlaps are obvious. Notice the medium column repeats "RAM" — that is the whole point: in-memory is a property most of them share, not a separate category.
| Named “type” | Location on path | Medium | Shared across instances? | Typical example |
|---|---|---|---|---|
| Client-side | User device (edge-most) | RAM + disk | No — one user | Browser HTTP cache, localStorage |
| DNS | OS / recursive resolver | RAM | Per resolver | Resolver A-record cache (TTL) |
| CDN | Edge PoP near user | RAM + disk | Yes — per PoP | CloudFront / Fastly object cache |
| Server-side (in-process) | App tier, inside the process | RAM | No — per instance | Caffeine (Java), sync.Map / Ristretto (Go) |
| Server-side (distributed) = “in-memory cache” | App tier, separate service | RAM | Yes — cluster-wide | Redis, Memcached |
| Database | Data tier | RAM | Yes — per DB node | Postgres buffer pool, result caches |
| Disk | Any host | Disk (cached in RAM by OS) | Per host | OS page cache, on-disk asset cache |
Worked example: one product page, three warmth states
A user in London opens https://shop.example.com/p/42. The origin app + database live in Virginia. Watch what each cache layer removes from the total. Latencies are representative round-trips, not guarantees.
Request 1 — cold (every cache misses)
| Step | What happens | Cost | Cumulative |
|---|---|---|---|
| 1 | DNS: resolver has no record → recursive lookup | 60 ms | 60 ms |
| 2 | TCP + TLS handshake to CDN edge (London PoP) | 30 ms | 90 ms |
| 3 | CDN edge miss → fetch across the Atlantic to origin | 150 ms | 240 ms |
| 4 | App L1 in-proc miss → Redis miss | ~1 ms | 241 ms |
| 5 | DB buffer pool miss → read page from SSD | 8 ms | 249 ms |
| 6 | Render + serialize HTML | 20 ms | ~269 ms |
On the way back, the response seeds every layer it passes: DB caches the page, Redis caches the rendered page fragment, the edge caches the HTML, the browser caches static assets. Note the choice hiding in that sentence: what you cache in Redis — the raw row or the rendered fragment — decides whether a warm Redis hit also skips render time.
Requests 2 & 3 — warm
| Scenario | Which caches hit | Total for the page |
|---|---|---|
| Warm — CDN edge hit (same user, seconds later; edge TTL not expired) | DNS cached (0 ms) + edge hit (15 ms) | ~15 ms (18× faster) |
| Edge TTL expired, app warm (edge revalidates but Redis + DB are hot) | DNS 0 + cross-Atlantic 150 + Redis 0.8 → skips SSD + render | ~151 ms |
The lesson is which layer buys what: for a far-away user the CDN edge is the giant win (removes the 150 ms ocean crossing); once you're at the origin, Redis and the buffer pool shave the 8 ms disk read and 20 ms render but can't touch network distance. You add caches where the biggest cost is, not everywhere.
How each layer actually works (organized by the path)
- Browser / client-side. The server sends
Cache-Control: max-age=86400; the browser stores the asset keyed by URL and serves it with zero network until it expires. Cheapest possible hit, but you can never invalidate it early — the copy is on a machine you don't control. - DNS. A resolver caches the domain→IP mapping for the record's TTL, skipping the multi-hop recursive walk (root → TLD → authoritative). A hit removes ~20–120 ms of lookups. The catch: a low TTL means fast failover but more lookups; a high TTL means fewer lookups but stale IPs during an outage.
- CDN edge. Hundreds of PoPs each hold a RAM+disk copy of your static and cacheable-dynamic content. A hit turns a cross-continent trip into a metro-distance one (150 ms → 15 ms). Keyed by URL (plus a configured vary set).
- Server-side, in-process (L1). A map inside the app process (Caffeine, Ristretto). Hits are ~1 µs because there is no serialization and no network — but the copy is per instance, so 10 pods = 10 independent caches that can disagree.
- Server-side, distributed (L2 / "in-memory cache"). Redis or Memcached: one shared RAM store all instances read. Hit ~0.5–1 ms (a network round-trip + deserialize). One source of truth across the fleet, at the cost of a network hop and an extra service to operate.
- Database buffer pool. The DB keeps hot pages in RAM; a hit is ~0.1 ms vs ~8 ms for an SSD read. You mostly don't configure this per-key — you size the pool so the working set fits.
- Disk / OS page cache. The kernel transparently caches recently-read file blocks in free RAM. It's why the "disk cache" is often really a memory cache — the second read of a file rarely touches the platter/flash.
Pitfalls
- Treating the axes as one list. "Should I use in-memory or server-side caching?" is a malformed question — Redis is both. Ask instead: which hop (location) and shared or per-instance (medium/topology).
- Per-instance caches drift. An in-process L1 cache on 10 pods means a value updated on pod A stays stale on pods B–J until each entry expires. Users get different answers depending on which pod the load balancer picks. Fix with short L1 TTLs, a shared L2, or pub/sub invalidation.
- Client caches you can't recall. Ship a bad
app.jswithmax-age=31536000and it's pinned on users' machines for a year. This is why assets are content-hashed (app.9f3a.js) — a new build is a new URL, so the old cache entry is simply never requested again. - DNS TTL vs failover. A 24-hour TTL means a failed server keeps receiving traffic for up to a day after you repoint DNS, because resolvers worldwide still serve the cached IP.
- Caching per-user data at a shared layer. Cache a logged-in user's cart at the CDN or in Redis under a non-user-scoped key and you leak one user's data to another. Only cache shared/public content at shared layers; key private data by user or don't cache it there.
- Stacking caches multiplies staleness. With browser + CDN + Redis + DB each holding a copy, an update must propagate through all of them. The effective staleness window is the sum of the layers' TTLs, not any single one.
When to use which layer — and the trade-offs
The two decisions engineers actually agonize over:
Decision 1: In-process (L1) cache vs distributed (Redis) cache
Choose in-process (L1) when the data is read-mostly, small, and tolerant of brief per-instance staleness — feature flags, config, reference lookups. You gain the fastest possible hit (~1 µs, no network, no serialization) and zero extra infrastructure. It costs you consistency: every instance has its own copy, so N instances = N versions of the truth, and memory is duplicated N times. Cache size is bounded by one process's heap.
Prefer distributed (Redis/Memcached) when instances must agree, the dataset is too big for one heap, or you need the cache to survive deploys and restarts. You gain a single shared source of truth and cache-size independent of app memory. It costs you a ~0.5–1 ms network round-trip per hit, serialization overhead, and a new stateful service to run, monitor, and scale — plus a new failure mode (Redis down → thundering herd onto the DB).
The senior move is both: a tiny short-TTL L1 in front of Redis (near-cache / two-tier). L1 absorbs the hottest keys at microseconds; Redis backs it for consistency and capacity. You accept a small, bounded staleness window (the L1 TTL) to cut Redis traffic dramatically.
Decision 2: CDN edge caching vs origin/application caching
Choose the CDN edge when content is shareable across users and geographically distributed users are your latency problem — static assets, images, public pages, cacheable API responses. You gain the removal of network distance itself (the single biggest latency term for far users) and you offload traffic from your origin entirely. It costs you control: invalidation is eventually-consistent across PoPs (purges take seconds to minutes), and per-user or auth-gated content generally can't be cached there safely.
Prefer origin/application caching (Redis, buffer pool) when data is personalized, changes frequently, or must be strongly consistent. You gain fine-grained, instant invalidation and per-user correctness. It costs you the full network path to origin on every request — the CDN's headline win is exactly what you forgo.
Rule of thumb: cache public/shared content as close to the user as it can safely go; cache private/volatile content as close to the data as it needs to be. Push each piece of data to the outermost layer where it's still correct to cache it.
Takeaways
- The “types” of caching are two orthogonal axes — location on the request path and storage medium — not a flat list. "In-memory" and "server-side" describe different questions; Redis answers both.
- Caches compose in series along the path (browser → DNS → CDN → app → DB → disk). Each layer only helps if it's hit before the request reaches the next, so put caches where the biggest cost is: CDN for network distance, Redis/buffer pool for compute and disk.
- The core trade-off everywhere is speed & locality vs consistency & control: closer/per-instance = faster but staler and harder to invalidate; farther/shared = slower per hit but one truth.
- Never cache private data at a shared layer, and content-hash anything with a long client TTL — the caches you can't invalidate are the ones that hurt.
Re-authored and deepened for this guide. Synthesized from the MDN HTTP Caching documentation (Cache-Control, revalidation), Fastly and Cloudflare CDN caching guides, the Redis and Memcached documentation, PostgreSQL's shared-buffers/buffer-pool design notes, and the caching chapters of Alex Xu's System Design Interview and Martin Kleppmann's Designing Data-Intensive Applications. Latency figures are representative orders of magnitude for illustration, not benchmarks.
🤖 Don't fully get this? Learn it with Claude
Stuck on Types of Caching? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Build the mental picture, not memorization.
I just read a lesson on **Types of Caching** (System Design) and want to truly understand it. Explain Types of Caching 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.
Socratic — adapts to where you're stuck.
Teach me **Types of Caching** 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.
Active recall exposes what you missed.
Quiz me on **Types of Caching** 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.
Intuition + hook + flashcards for long-term memory.
Help me remember **Types of Caching** 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.