Sign in to keep reading
“Scaling Reads” is free with an account. Your progress and bookmarks follow you across devices.
Sign in to continueNEW: ML Mock & Coaching with a 20+ year experience coach. Save $20 on sessions for Premium.ML Mock & Coaching now available
NEW: ML Mock & Coaching with a 20+ year experience coach. Save $20 on sessions for Premium.ML Mock & Coaching now available
Techniques for handling read-heavy workloads including query optimization, read replicas, caching layers, and CDN distribution.
“Scaling Reads” is free with an account. Your progress and bookmarks follow you across devices.
Sign in to continueMost applications are read-heavy. Think about it: in the Yelp question, a restaurant page might get hundreds of views for every single review someone writes. In a Leaderboard, thousands of users check rankings every second, but scores only update when a game ends. These read-to-write ratios of 100:1 or higher are the norm, not the exception.
So what happens when your app gets popular? Your database starts struggling — not because your code is bad, but because a single machine has physical limits: finite CPU, memory, and disk speed. No amount of code optimization will let one server handle 100,000 queries per second when the hardware tops out at 10,000.
The good news: scaling reads is a well-understood problem with a clear progression. You apply solutions in order, each one building on the last. Don't jump to distributed caching on day one — start simple and add complexity only when needed.
Before adding any new infrastructure, make sure you're not leaving performance on the table. These optimizations are essentially free.
If you've written SQL before, you know about indexes — but it's easy to underestimate their impact. A single missing index on a high-traffic query can be the entire reason your database is slow.
What to index: columns used in WHERE, JOIN, and ORDER BY clauses. Run EXPLAIN ANALYZE on your slow queries — if you see "Seq Scan" (sequential scan) on a large table, that's your database reading every single row to find a match. An index turns that into a near-instant lookup.
What to watch out for: indexes aren't free. Each one consumes memory and slows down writes (because the index must be updated on every insert/update). Don't blindly index everything — focus on columns that appear in your most frequent queries.
Composite indexes (indexes on multiple columns) can be especially powerful. If your query filters on city and sorts by rating, a composite index on (city, rating) can answer the query entirely from the index without touching the actual table rows. This is called an index-only scan.
See it in practice: In Yelp, composite indexes on location and rating power the restaurant search. In Google Calendar, indexes on user ID and time range make event lookups fast. The Search and Full-Text Indexing pattern goes deeper into inverted indexes and Elasticsearch for when B-Tree indexes aren't enough.
In a normalized database, data is split across many tables to avoid duplication. That's clean and correct, but it means your read queries often need JOIN operations — and joins get expensive as tables grow.
Denormalization means pre-computing and storing the result so reads don't need to join at all. In the Yelp question, instead of computing a restaurant's average rating by scanning all its reviews on every page load, you store the average directly on the restaurant row and update it whenever a new review comes in.
The trade-off: your writes become slightly more complex (you need to keep the pre-computed value in sync), but when reads outnumber writes 100:1, this is almost always worth it.
See it in practice: In Yelp, avg_rating and review_count are denormalized onto the restaurant row to avoid scanning the reviews table on every page load. In Leaderboard, player ranks are pre-computed in Redis sorted sets rather than recalculated from raw game results. In Slack, unread counts and last-message previews are denormalized for instant channel list rendering.