CMD Guide
HomeSystem DesignSystem Design Trade-offs

Read Heavy vs Write Heavy System

A workload is read-heavy or write-heavy because of which operation you can cheaply make more copies of: reads can be fanned out to any number of identical stale-tolerant replicas and caches, so you scale reads by duplication; writes must converge to a single authoritative order (one row, one log, one committed sequence), so you scale writes by reducing per-write cost — batching, appending sequentially, and deferring work. That asymmetry is why the two are tuned in opposite directions, and why bolting read tactics onto a write problem (or vice versa) fails.

The signal is the read:write ratio measured in operations per second, not row counts. Content and social feeds typically run 10:1 to 1000:1 reads-to-writes (a tweet is written once, read millions of times); metrics/telemetry, event logs, and IoT ingestion invert it — near 1:50 or worse. Above roughly 95% reads you optimize the read path first; below roughly 80% reads (a sustained write flood) the write path is the bottleneck and no amount of caching helps.

Worked trace 1 — a read-heavy feed (200:1)

Take a news/social service at peak: 50,000 read QPS and 250 write QPS (a 200:1 ratio). A single Postgres primary handles maybe a few thousand simple queries/sec before CPU and buffer contention saturate it, so 50,250 QPS on one node is impossible. Layer the read tactics and watch the load collapse:

StageQPS reaching itWhy
Redis cache (95% hit)50,000 in → 47,500 servedHot articles live in RAM, ~0.3 ms each; only misses fall through
5 read replicas2,500 misses → ~500 eachLoad balancer spreads the 5% miss traffic; each replica now trivially loaded
Primary250 writes onlyReads never touch it; it just commits writes and ships WAL to replicas

The whole point: the primary went from an impossible 50,250 QPS to a comfortable 250. Cache did the heavy lifting (95% absorbed in RAM); replicas mopped up the misses; the primary is reserved for the one thing that cannot be duplicated — the authoritative write.

diagram
diagram

The trap nobody sees in the diagram: read-your-writes vs replica lag

Replicas are asynchronous: the primary commits, returns success, then ships the WAL. Under normal load a replica trails by tens of milliseconds; but most engines apply replication on a single thread, so a write burst or one long transaction can push lag to seconds or minutes. Now combine that with a load balancer that sends the user's next read to a random replica:

t (ms)EventWhat the user sees
0User POSTs a comment → primary commits, returns 200"Posted!"
5Page reloads → LB routes the GET to replica R3
5R3 has only applied WAL up to t = −150 ms (150 ms behind)Comment is missing
210R3 finally applies the commentRefresh shows it — user already re-posted, now duplicated

This is a read-your-writes violation, and it is the single most common production bug of the replica pattern. Fixes, cheapest first: (1) after a user's write, pin their reads to the primary for a few seconds (session stickiness); (2) return the commit's log position (LSN) to the client and require the serving replica to have applied ≥ that LSN before answering (monotonic/session consistency); (3) write the new value into the cache on the write path so the user reads their own write from RAM. Budget the lag explicitly — alert at >1 s, page at >5 s — because lag is invisible until a user complains.

Worked trace 2 — write-heavy ingestion (1:50)

Now flip it: a metrics/telemetry service ingesting 200,000 writes/sec with only a trickle of dashboard reads. Caching and replicas are useless here — every write must be durably recorded, and the bottleneck is fsync. An SSD fsync costs ~1 ms; a naive "commit each write with its own fsync" loop is therefore capped near 1,000 writes/sec, 200× short. Two mechanisms rescue it:

Combine both plus sharding across, say, 8 nodes and 200,000 writes/sec becomes ~25,000/node — well within LSM range. The reads that do happen are the price you pay (see pitfalls).

diagram
diagram

Pitfalls

When to use it / when NOT — and the trade-offs

Reach for the read-heavy stack (cache + replicas) when the ratio is ≥95% reads, the same keys are hit repeatedly (high cache locality), and users tolerate seconds of staleness. Do not when reads are unique/uncacheable (analytical scans over arbitrary ranges — a cache never warms) or when correctness demands the freshest value on every read (bank balances before a transfer).

Cache (Redis) vs read replica — both offload reads, but differently. A cache gives sub-millisecond RAM reads and huge fan-out relief, but you own invalidation, stampede risk, and it only serves what you thought to cache. A replica speaks full SQL (arbitrary queries, joins), needs no invalidation logic, but costs a real query per read and carries lag. Choose the cache when a small hot set dominates and staleness is fine; prefer replicas when queries are diverse and you can't enumerate cache keys — and in practice, layer both.

Reach for the write-heavy stack (LSM + batching + async + sharding) when writes are the sustained bottleneck, writes are append-like (events, metrics, logs), and reads are few or tolerate higher latency. Do not when you need fast point/range reads on the freshest data with strong consistency, or when write volume actually fits a single tuned B-tree primary — the LSM's read penalty and operational weight aren't worth it.

LSM vs B-tree — the core write decision. LSM turns random writes into sequential appends (far higher ingest, lower write latency) but pays with read amplification and constant background compaction (CPU, I/O, occasional stalls). A B-tree gives fast in-place reads and simple operations but does random-write I/O with high write amplification that collapses under a write flood. Choose LSM when ingest dominates and reads are point-lookups or scans that tolerate a few SSTable probes; prefer a B-tree when reads are the hot path and writes fit the primary.

CQRS vs one shared model. Splitting writes (command) from reads (query) lets each side scale and be modeled independently — but you inherit eventual consistency between them, a projection/sync pipeline, and roughly double the moving parts. Choose CQRS when read and write shapes genuinely diverge at scale; prefer one model when a single well-indexed store still keeps up — most systems never need CQRS.

The senior instinct: measure the ratio and the tail latency budget first, then add the cheapest tactic that clears the bottleneck (usually a cache for reads, batching for writes) before reaching for replicas, sharding, or CQRS — each of which buys scale by spending consistency and operational simplicity.

Takeaways


Re-authored/Deepened for this guide. Draws on Martin Kleppmann, Designing Data-Intensive Applications (replication lag, read-your-writes and monotonic-read consistency, B-tree vs LSM storage engines); the PostgreSQL documentation on WAL and group commit; Apache Cassandra and RocksDB design notes on LSM-tree write/read amplification and compaction; and Redis documentation on caching patterns and stampede mitigation.

🤖 Don't fully get this? Learn it with Claude

Stuck on Read Heavy vs Write Heavy System? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Read Heavy vs Write Heavy System** (System Design) and want to truly understand it. Explain Read Heavy vs Write Heavy System from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Read Heavy vs Write Heavy System** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Read Heavy vs Write Heavy System** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Read Heavy vs Write Heavy System** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes