SD System Design In Depth
0%
lesson system-designrelational-databasesscaling

Relational database scaling

Module 2.1: Relational Modeling & Query Execution Updated Sep 17, 2026
📄 Cheat Sheet
7 Must-know Strategies to Scale Your Database
ByteByteGo 7 Must-know Strategies to Scale Your Database Watch on YouTube ↗

Covers the same ladder as the lesson (indexing, vertical scaling, caching, replication, partitioning, sharding) with clear diagrams and when to use each.

0:00 / --:--

Under the Hood: Read Replicas vs. Horizontal Sharding

Scaling a relational database follows a predictable operational progression as traffic and data volume expand beyond single-host physical limits:

The Progression of Relational Scaling

flowchart TD Step1["1. Vertical Scaling: Upgrade CPU / RAM / NVMe"] --> Step2["2. Read Replicas: Asynchronous WAL streaming (Offload SELECTs)"] Step2 --> Step3["3. Functional Decomposition: Separate Users, Orders, Billing into distinct DBs"] Step3 --> Step4["4. Horizontal Sharding: Partition single table across N database nodes"]

Replication Lag & Read-Your-Own-Writes

Adding read replicas offloads read volume. However, because replication is asynchronous, replicas lag behind the primary by tens or hundreds of milliseconds. If a user updates their profile and immediately refreshes the page, routing the read to a replica returns old data.

Read-Your-Own-Writes Pattern: The application tracks the user's latest mutation timestamp. If now() - last_write < replication_lag_threshold (e.g., 2 seconds; a heuristic, see the LSN-based version below), route the read to the primary database; otherwise, route to read replicas.

Horizontal Sharding Challenges

  • Cross-Shard Joins: Joining data across shards requires distributed scatter-gather queries in application memory, destroying performance.
  • Two-Phase Commit (2PC): Atomic multi-shard transactions add at least one extra network round trip and a forced log write per participant, hold locks for that whole window, and block participants if the coordinator fails.
  • Re-Sharding: Doubling shards requires moving terabytes of data over live production traffic without write downtime.

Worked example: when does each rung stop working?

Suppose an app has 30,000 reads/s, 3,000 writes/s, 4 TB of data growing 150 GB/month, and a primary on 32 vCPUs.

  • Vertical: moving to 96 vCPUs and 768 GB of RAM keeps the hot set cached. That buys perhaps 2-3x on reads, less on writes if WAL fsync or lock contention is the limit.
  • Replicas: 4 replicas at about 10k reads/s each absorb reads easily. Writes are unchanged, because every replica must replay all 3,000 writes/s.
  • Functional split: moving the analytics/events tables (60% of writes) to their own cluster or a columnar store leaves the primary at about 1,200 writes/s.
  • Sharding: needed only if single-primary writes, storage (for example over 10-20 TB per node, where backups and restores take too long), or blast radius become the limit.

Trade-off table

TechniqueScalesYou loseTypical operational cost
Vertical scalingEverything, up to hardware limitsNothing, except cost and one big failure domainLow
Read replicasReadsRead-your-writes on replicasLow-medium: lag monitoring, routing
CachingHot readsFreshness; invalidation complexityMedium
Table partitioning (same node)Maintenance: vacuum, index size, retention dropsSome cross-partition uniquenessLow-medium
Functional splitWrites per domainCross-domain joins and transactionsMedium
Horizontal shardingWrites and storage, near-linearlyCross-shard joins, global constraints, simple transactionsHigh: routing, resharding, schema changes on N shards

Read-your-writes, done precisely

A time threshold ("route to the primary for 2 s after a write") is a heuristic. A precise version records the primary's WAL position after the write (pg_current_wal_lsn()) in the user's session, and reads from a replica only if its pg_last_wal_replay_lsn() is at or beyond that position. MySQL offers the equivalent with GTIDs (WAIT_FOR_EXECUTED_GTID_SET).

How real companies climbed the ladder

  • Figma: vertical partitioning by table groups first, then horizontal sharding with a custom proxy (DBProxy), keeping Postgres.
  • Notion: sharded Postgres by workspace ID into 480 logical shards on 32 physical hosts, later expanded to 96.
  • Shopify: "pods" that each hold a set of shops in their own MySQL, and moves shops between pods to fix hotspots.
  • Instagram: thousands of logical shards mapped onto fewer Postgres servers, with IDs embedding the shard number.
Check yourself Question 1 of 3
Read-your-own-writes routing recall
From: Replication Lag & Read-Your-Own-Writes

A user saves their profile and immediately reloads the page. Replicas lag the primary by up to a few hundred milliseconds. What does the read-your-own-writes pattern do?

Key Takeaways

Read replicas scale reads but introduce asynchronous replication lag.
Enforce Read-Your-Own-Writes by routing recent writers to the primary database.
Horizontal sharding provides horizontal scale but sacrifices cross-shard joins and ACID atomicity.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue