48. Database scaling strategies
Answer as submitted
“Yes — I apply these scaling strategies together, choosing based on measured bottlenecks rather than preference.”
Expanded answer
Yes, and the discipline is in the order rather than in the list. Measure first: the p95 and p99 of the endpoints that matter, the hit ratio of every cache layer, the replication lag, and the wait types consuming the database's time — with the constraint written down before anything changes, because a scaling change with no baseline is a change nobody can attribute a result to. Read replicas take the read-heavy traffic that genuinely tolerates a stale answer: listings, reports, analytics. Every read path declares its lag tolerance, a read that follows the caller's own write is pinned to the primary for a short window, and the replication lag is a monitored number with a budget and an automatic fallback — a replica beyond its budget is not used, because serving a slightly slower answer from the primary is better than serving a stale one and letting the user find out. Caching is layered so a hit at the cheapest layer costs nothing beyond it: in-process memoisation for the hottest immutable data, Redis with cache-aside for shared state and write-through where staleness is unacceptable, HTTP caching with validators, and a CDN for anonymous traffic. Every entry declares its TTL, its invalidation contract and its stampede protection; the invalidation contract is written before the cache is added, and the hit ratio is measured per layer rather than assumed. Offload before splitting: a read model for the expensive aggregation, a materialised view refreshed on a schedule, cold data archived by partition switch, pooling and batching to remove round trips, and search or analytics moved to an engine built for it — which together frequently remove the need to shard at all. Sharding last, because it is the point of no return: every query becomes shard-local or a scatter-gather, transactions stop spanning shards, and the key choice is effectively permanent. The key is chosen from the access pattern with the cross-shard cost written down and a rebalancing plan agreed before the first shard exists; in DynamoDB or Cassandra the same distribution is expressed directly in the partition key, which makes hot-partition avoidance the same discipline under a different name. And in all cases the new topology is validated by a load test that includes the two unpleasant scenarios — an empty cache and a cache that has failed entirely — before it is released.
| Rung | What it is for | What it costs |
|---|---|---|
| Measure | naming the constraint: latency percentiles, hit ratio, replication lag, lock waits, pool saturation | the effort of not guessing, which is where most of the value is |
| Cache | absorbing repeated reads at the cheapest possible layer | a second copy of the truth, an invalidation contract, and a cold-start scenario to test |
| Read replicas | moving lag-tolerant reads off the primary | lag to monitor, read-your-writes to pin, and a fallback path to prove |
| Offload | removing the work entirely: read models, materialised views, archival, batching, separate engines | a projection to keep current, and a report that is seconds slower but stops competing with checkout |
| Shard | the last resort when a single primary's write volume or working set is genuinely the limit | cross-shard queries, no cross-shard transactions, and a key choice that is effectively permanent |
| Validate | load-testing the new topology, including an empty and a failed cache | test time, and the discipline of recording the result against the decision |
Live scaling-ladder workflow proof
The running component below changes its rung/result view through real event binding. Select a rung to see the decision, the reasoning behind it, and the number that justifies it.
Choose a rung of the scaling ladder
Select a rung to inspect the decision, the reasoning and the number behind it.
$ p95 and p99 latency · cache hit ratio per layer · replication lag · lock waits by type · the constraint named before the fix
A scaling change answers a measured constraint rather than a feeling: the p95 and p99 of the endpoints that matter, the hit ratio of every cache layer, the replication lag in seconds, and the wait types holding work up on the database. The bottleneck is written down before anything changes, because a scaling change without a baseline is a change nobody can attribute a result to.
scaling decisions taken with a baseline: 100% constraint named in writing before the change: yes "the database is slow" accepted as a diagnosis: never signals watched: p95, p99, hit ratio, lag, lock waits, pool saturation
What the ladder buys
Source files
Select a file to inspect the proof with syntax highlighting and line numbers. The first file is the code running the component above; the others are the read routing with its lag budget and read-your-writes pinning, the cache inventory with its invalidation contracts, and the load test that validates the topology together with the record of every scaling decision.
Acceptance criteria
- Covers read replicas, sharding and caching, and states the method that chooses between them.
- Declares the lag tolerance of every read path and pins read-your-writes to the primary.
- Shows an interactive proof of choosing a rung and reading the number behind it.
- Requires an invalidation contract before a cache exists, and measures the hit ratio per layer.
- Treats sharding as the last rung, with the key and the rebalancing plan agreed in advance.