Patterns
Scaling Reads
Most systems read far more than they write. How to serve that read load — caching, read replicas, denormalization and CDNs — and the staleness you accept for each.
The great majority of real systems are read-heavy: a post is written once and read a million times, a product is updated rarely and viewed constantly. That's good news, because reads are the easy thing to scale — you can make copies of data freely, as long as you're willing to let those copies lag a little. This pattern is the toolbox, cheapest first.
1. Cache the hot reads
The first and biggest lever. Put the frequently-read, rarely-changed results in memory (Redis) in front of the database. A cache hit skips the database entirely. For public data you can push the cache even further out — see CDNs below. Caching is covered in depth in Caching; the one-liner is: trade bounded staleness for a huge drop in read load.
2. Add read replicas
When the database itself is the bottleneck on reads, add replicas: copies that receive a stream of changes from the primary. Send all writes to the primary and spread reads across the replicas.
writes ──► [ Primary ] ──replication──► [ Replica 1 ] [ Replica 2 ]
reads ───────────────────────────────► (round-robin the replicas)Because most load is reads, this removes most of it from the primary. The catch is replication lag — a replica can be a fraction of a second behind, so a user who just wrote something might read a replica that hasn't caught up and not see their own change. The fix is read-your-own-writes: for a short window after a user writes, route their reads to the primary. And keep a documented list of reads that must always hit the primary (anything used to make a money decision).
3. Denormalize the read path
If a hot read needs an expensive join every time, precompute it. Store the joined/aggregated shape the read wants (a feed row with the author's name baked in; a follower_count column kept up to date on follow/unfollow). The read becomes a single cheap lookup. The cost is keeping the precomputed copy in sync — a write-time job updates it. You're moving work from read-time (often, hot) to write-time (rarely).
4. Push static and public reads to a CDN
For images, assets, and public pages or API responses, a CDN serves a cached copy from an edge location near the user. These reads never reach your servers at all — the cheapest read is the one your infrastructure never sees. Set Cache-Control headers and let the edge do the work.
Putting it in order
| Lever | Removes load from | Staleness you accept |
|---|---|---|
| CDN / HTTP cache | Your servers entirely | Up to the TTL, public data |
| Redis cache | The database | Bounded by TTL + invalidation |
| Read replicas | The primary | Replication lag (ms) |
| Denormalization | Join/compute at read time | Copy lag until the sync job runs |
Reach for them in roughly that order — each is cheaper and less invasive than the next one down.
What to say in an interview
Establish the read:write ratio, then layer the cheap levers:
"This is read-heavy — roughly 100:1. I'd start by caching the hot product reads in Redis with a short TTL, and serve images and public pages from a CDN so they never touch my servers. If the database is still read-bound I'd add read replicas and route reads to them, keeping 'read-your-own-writes' on the primary for a few seconds after a user posts. Checkout stock always reads the primary."
Next up: Scaling Writes — the harder half, since you can't just make copies.
