2,500
database reads a second at 95% hits
Origin load = reads × (1 − hit ratio), so 50,000 × 0.05. The mean falls from 20 ms to about 2. At 99% hits, just 500.
“PostgreSQL already caches it in memory”
Bad: Buffer cache is enough
It saves disk reads, not work: every query still crosses the network and gets planned.
Good: Cache the answer
Answer from memory and skip the query entirely.
Detect it: the same call, with the same key, all day
3 million product lookups a minute across 12,000 products is a repeat factor of 250. A factor in the hundreds says a cache will pay.
Cache-aside: check Redis, load on a miss, store with a TTL
Key by product id, store JSON, set a 5-minute TTL plus jitter, delete after each committed write, and remember missing ids for 30 s.
Rules that keep a cache honest
- Delete after the commit, never before
- Jitter TTLs so keys don’t expire together
- Cache “not found” for 30 seconds
- Coalesce misses so a hot key loads once
At 95% hits the database sees 2,500 reads a second. Then Redis restarts mid-sale.
The cliff
Load jumps 20×, to 50,000 a second. Cap misses at what the database takes; serve stale or shed.
Let browsers and the CDN cache for you
For 60 s any cache answers for you. For 30 s more it serves stale while refreshing; an ETag makes that a bodiless 304.
Say it like this
Cache what’s read far more than it changes, with a TTL the business chose. Delete after commits, coalesce misses, use HTTP caching, and size the origin for the cache dying.