Courses › System Design › Patterns

Scaling Reads

Indexes, read replicas, caching layers and CDNs — in that order.

Most systems are read-heavy — often 10:1 to 1000:1 reads to writes. When reads become the bottleneck, apply fixes in order from cheapest to most complex. Interviewers like to see that order.

1. Optimize the database first

  • Add the right indexes. A missing index is the most common cause of slow reads. See Database Indexing.
  • Fix the queries. Avoid N+1 queries; fetch only needed columns; paginate with cursors.
  • Denormalize hot paths. Precompute counts or embed frequently joined fields so a read is a single lookup.
  • Scale vertically. More RAM means more of the working set lives in the buffer cache.

2. Read replicas

Replicate the primary to one or more read-only replicas and send reads there.

[App] -> (Primary DB)
(Primary DB) -> (Replica 1)
(Primary DB) -> (Replica 2)
[App] -> (Replica 1)
  • Scales read throughput nearly linearly.
  • Replication is usually asynchronous, so replicas lag by milliseconds to seconds. Handle read-your-writes: send a user's reads to the primary for a short window after they write, or read from the primary for anything that must be fresh.

3. Caching

Put a cache in front of the database for hot data (see Caching):

  • Distributed cache (Redis) for shared, computed or frequently-read objects
  • In-process cache for tiny, extremely hot data
  • Plan for invalidation, stampedes and hot keys

4. CDN and edge caching

Serve static assets, media and even cacheable API responses from servers close to users. This removes load from your origin entirely and cuts latency for global users.

5. Precompute and materialize

When a read requires expensive computation (a personalized feed, a leaderboard, analytics), compute it ahead of time on write or in the background:

  • Fan-out on write for feeds (see Design a News Feed)
  • Materialized views maintained by stream processors
  • Search indexes (Elasticsearch) built from the primary database

6. Shard

If the data or read load outgrows replicas and caches, partition it (see Sharding). This is last because of the complexity it adds.

Handling hot keys

Even a well-scaled system breaks when millions of users read the same item — a viral post, a celebrity profile:

  • Replicate the hot key across multiple cache nodes and pick one randomly
  • Add a short-lived in-process cache on each app server
  • Use request coalescing so only one request per server fetches a missing key

[!INTERVIEW] Narrate the progression "Event pages are read-heavy. First I'd make sure we have an index on event_id. Then cache event details in Redis with a short TTL, and serve images from a CDN. If the database is still hot, I'd add read replicas, accepting a second of lag for browsing."