Diagnosing whether the real bottleneck is read throughput, write throughput, or storage — and the matching fix for each, plus why premature sharding is a common self-inflicted mistake.
Published September 23, 2026
Every technique in this chapter (indexing, denormalization, read replicas, sharding) is a specific fix for a specific bottleneck — applying the wrong one for the actual problem wastes engineering effort and sometimes makes things worse. This lesson is the diagnostic step that should come before reaching for any of them.
Before picking a technique, determine which of three genuinely different problems you're facing — they call for different fixes, and misdiagnosing wastes real engineering effort on the wrong solution:
These are diagnosable directly from monitoring: read-heavy query latency climbing while write latency stays flat points at #1; write latency/queue depth climbing points at #2; disk usage approaching capacity with throughput otherwise fine points at #3 — conflating them (e.g. sharding to fix what's actually a missing-index read problem) is a common, expensive misdiagnosis.
The cheapest fix for read pressure is almost always caching (an application-level cache, or a materialized view — see Denormalization & Schema Trade-offs) in front of the database, since it avoids touching the database at all for cache hits. Only once caching's ceiling is reached (data that genuinely can't be cached effectively — highly dynamic, low-repeat-read data) does adding read replicas (see Sharding vs Partitioning vs Replication) become the next step, scaling read capacity horizontally without touching write architecture at all.
Caching doesn't help write throughput at all (a write still has to reach the database eventually). The real options: sharding (spreading writes across multiple independent database instances, see Sharding vs Partitioning vs Replication) for a genuine, sustained write-throughput ceiling, or an async write-behind pattern (writes go to a fast, durable queue first — e.g. Kafka — and get applied to the database asynchronously by a consumer, decoupling the caller's response time from the database's actual write latency) when writes can tolerate a small propagation delay before being durably reflected in the primary store.
If the data itself is simply too large, the fix isn't more compute or more replicas — it's reducing what's actively stored on expensive, fast storage. Archiving moves genuinely cold, rarely-accessed data to cheaper storage (a separate archive table, cold object storage) entirely out of the hot path. Partitioning by time (see Sharding vs Partitioning vs Replication) makes this practical to execute cleanly — dropping or archiving an entire old partition is far cheaper than deleting matching rows out of one giant unpartitioned table.
Sharding is the most operationally expensive technique in this chapter — it adds cross-shard query complexity, operational overhead (more instances to manage, monitor, and keep available), and often irreversible schema/application changes to support a shard key everywhere. Reaching for it before confirming that caching, indexing, and read replicas are genuinely insufficient is a common, costly mistake — the honest, unglamorous fix for most read-heavy bottlenecks is caching and a missing index, not a sharding migration, and skipping straight to the most expensive tool without first exhausting the cheaper ones is exactly the over-engineering pattern this framework's diagnostic-first ordering is meant to prevent.
Q: How would you tell whether a read-heavy bottleneck is caused by a missing index rather than genuinely needing a cache or replica? A: Check query execution plans first (see Query Execution Plans) for Seq Scans on large tables — a missing index is a far cheaper, more surgical fix than adding infrastructure (a cache layer or a replica), and it's worth ruling out before assuming the problem requires a bigger architectural change.
Q: Can write-heavy and read-heavy bottlenecks coexist, and does the framework still apply? A: Yes, and the framework still applies per-bottleneck — a system can genuinely need both read replicas AND write sharding simultaneously; the value of diagnosing each direction separately is applying the right fix to each, rather than reaching for one technique (like sharding) and assuming it solves both problems at once, when it primarily addresses write/storage scale, not read latency.
Q: Is async write-behind ever inappropriate, even for a genuine write bottleneck? A: Yes — whenever the caller genuinely needs confirmation the write is durably applied before proceeding (a financial transaction, for instance), introducing an async gap between 'accepted' and 'actually persisted' changes the consistency guarantee in a way that may be unacceptable for that specific write, regardless of the throughput benefit it would otherwise offer.
Q: What's a concrete sign a team sharded prematurely? A: Significant cross-shard query complexity and application code contorted around shard-awareness, for a dataset/throughput level that a single well-indexed, well-cached, replica-backed database could still comfortably handle — the tell is usually that the operational cost of sharding clearly outweighs any throughput problem it was meant to solve, discoverable in hindsight by checking whether the pre-sharding bottleneck was ever actually confirmed via the diagnostic steps above.