Loading...
Loading...
The last topic ended with one shared store turning 10,000 database reads into 1,000 at a 90% hit rate, then asked where else the sticky notes live. You added Redis, a fast shared in-memory store every server reaches. Hit rate looks great. Then someone asks why the homepage still takes two seconds, and you realize the browser re-downloads the same logo, the CDN refetches the same stylesheet, and the database re-runs the same count query, because each layer only remembers its own job. One cache fixed one hop. A request crosses five.
Real systems cache in layers, cheapest check first: the user’s own device, then a nearby edge, then shared memory, then the database’s own buffers, then disk. Each layer catches what the one above missed.
Images, stylesheets, scripts, ~0ms, helps exactly one person
Static assets for everyone nearby, served from a network of cache servers near users
Query results, sessions, every server shares it
Hot pages kept in RAM by the database engine itself
The truth, at full price
Costs you nothing, the user’s disk does the work. But it helps only that one user, and you control it with headers from afar (Cache-Control: max-age=3600). Perfect for logos and bundles. Useless for “show me my orders.”
Same file for everyone nearby, marketing pages, public API responses, product images. Dies the moment content turns personal.
The layer you operate. Query results, sessions, computed aggregates, full control over keys, expiry, and invalidation. The pattern never changes:
GET user:123 || db.find() + SETThe InnoDB buffer pool, the memory area MySQL's default storage engine uses for hot pages, and the shared buffers of Postgres, its equivalent page cache, keep hot pages in RAM without asking you. Free performance with zero control, tune memory size, then stop thinking about it.
| Shelf | Speed | Helps whom | You control |
|---|---|---|---|
| Browser | ~0ms | One person | Headers only |
| CDN | ~20ms | A region | Purge rules, TTLs |
| Shared memory | ~1–5ms | Every server | Everything |
| DB buffers | ~10–50ms | The database | Memory size, roughly |
Layered hit rates multiply like sieves. Say the browser hits 30% of the time at ~0ms, the CDN catches 50% of the remainder at ~20ms, and shared Redis catches 80% of what is left at ~3ms, only about 7% of requests ever reach the database at ~100ms. The blended average lands near 0.5 × 20 + small change ≈ 12–15ms, versus 100ms with no layers at all. Each layer's job is precisely this: shrink the remainder for the expensive layer below it.
Coherence is the tax. A price change must clear the browser (uncontrollable until max-age expires), the CDN (purge in seconds to minutes), Redis (delete on write), and the database buffers (automatic), four TTLs, time-to-live expiries that bound how long a copy may live, that must agree on how stale “acceptable” is. Teams that set browser max-age to a year for fast-changing content learn this from user complaints, not dashboards: the fastest layer is also the hardest to invalidate.
Layered staleness looks like haunted behavior: the user updates their avatar, sees the old one, hard-refreshes, sees the new one, then sees the old one again on the next page. What happened is each layer refreshed on its own schedule: Redis picked up the write immediately, the CDN revalidated a minute later, the browser held its copy for an hour. Support cannot reproduce it because their browser holds a different generation than the reporter's.
Double-caching wastes the memory you paid to save: storing full HTML pages in Redis and the identical fragments in per-server local caches, or caching the same JSON at the CDN and byte-identical in Redis with different keys. Audit what each layer uniquely absorbs, if removing one layer changes nothing in origin traffic, it is decoration, not defense.
Every layer has a sizing rule of thumb. Browser caches hold megabytes per user for free, spend it on hashed static assets, never on API responses you cannot purge. CDN capacity is effectively unbounded for your purposes; the constraint is key cardinality, the count of distinct cache variants, so keep variant counts (languages, encodings, device classes) in the single digits. Redis is the layer you actually provision: size to 1.5–2× the hot working set so evictions stay rare, watch the evicted-keys rate as the leading indicator, and shard, splitting data across nodes, past ~25GB per node or ~50k ops/sec per instance rather than scaling a single primary vertically into latency trouble.
Database buffers deserve one deliberate decision: give Postgres, an open-source relational database, roughly 25% of system RAM for its shared buffers (up to ~8–16GB) and the InnoDB buffer pool, MySQL's page cache, up to 70–80% on a dedicated database host, then stop. These caches are automatic and uninspectable per key, tuning beyond memory size means fighting the engine instead of using the layers you control.
As close to the user as correctness allows: immutable bytes in the browser and the edge network, shared computed answers in Redis, hot working sets in database buffers, each layer shrinking the remainder for the one below. Every layer added divides origin load and multiplies stale-copy combinations, so shared mutable state gets exactly one authoritative cache with explicit invalidation, and the outer layers hold only what invalidation can reach. That is also why a long time-to-live on everything with purge-on-change is not a universal answer: browser copies cannot be purged at all, edge purges take seconds and cost per-operation attention, and a missed write path leaves the long-lived lie in place until expiry. Long TTL plus purge fits content with rare, well-tracked writes such as product images; it fails content with frequent or scattered writes such as feeds and counts, where short TTLs bound the lie without depending on perfect invalidation.
Layers divide origin load and multiply stale-copy combinations: a price cut from $49 to $39 at noon sits in the browser for an hour, at the edge for minutes, in Redis until deleted, and in database buffers until evicted, with four TTLs disagreeing about how stale is acceptable. The open question is how those copies stay honest when truth changes, because expiry schedules alone only bound the lie. Four strategies pick four different points on freshness, speed, and simplicity.