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

LeverRemoves load fromStaleness you accept
CDN / HTTP cacheYour servers entirelyUp to the TTL, public data
Redis cacheThe databaseBounded by TTL + invalidation
Read replicasThe primaryReplication lag (ms)
DenormalizationJoin/compute at read timeCopy 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.