Sang's Blog

Replication and sharding, consistency at scale

A database cannot handle the load. Queries are slow, writes are blocking, connections are timing out. Replication offers a solution: create read replicas, route read traffic to them, and suddenly there is 10x the read capacity. The implementation works—for a while.

Then a user updates their profile, but when they refresh the page, the old data is still there. They refresh again, and now it is updated. The read replica is lagging behind the primary. The scaling problem is solved, but a consistency problem has been created.

Replication works by copying data from a primary node to one or more replicas. Writes go to the primary, reads go to the replicas. The primary sends changes to the replicas, either synchronously or asynchronously. Synchronous replication guarantees consistency but adds latency because the write must wait for all replicas to acknowledge. Asynchronous replication reduces latency but allows replicas to lag behind the primary. The choice is between consistency and performance. There is no free lunch.

The consistency problem is not just theoretical. When a user writes data and immediately reads it, they expect to see their write. If the read goes to a replica that has not yet received the write, they see stale data. This is called read-your-writes consistency, and it is the minimum expectation users have. Achieving it requires either synchronous replication (which hurts performance) or routing reads to the primary for a short window after a write (which adds complexity).

Sharding works by partitioning data across multiple nodes. Each node holds a subset of the data, determined by a sharding key. Writes and reads are distributed across shards, so no single node is a bottleneck. A sharding strategy must be chosen: hash-based, range-based, or directory-based. Each strategy has trade-offs. Hash-based distributes evenly but makes range queries impossible. Range-based supports range queries but can create hotspots. Directory-based is flexible but requires a lookup service.

The sharding problem is not just partitioning. It is coordination. When data that spans multiple shards needs to be queried, each shard must be queried and the results aggregated. This is expensive and complex. When uniqueness across shards must be maintained (like a unique email address), coordination between shards is required or it must be accepted that uniqueness is only guaranteed within a shard. When shards need to be rebalanced because one node is overloaded, data must be moved between nodes without downtime. Each of these problems is a distributed systems problem.

The misconception is that replication and sharding are scaling solutions. They are not. They are distributed systems solutions. Scaling is about handling more load. Distributed systems are about coordinating state across multiple nodes. Replication and sharding convert the scaling problem (how to handle more load?) into a distributed systems problem (how to coordinate state across multiple nodes?). And distributed systems problems are harder than scaling problems.

This is why replication and sharding should be last resorts, not first choices. Before replicating, queries should be optimized. Indexes should be added. Frequently accessed data should be cached. Schemas should be denormalized. Before sharding, data should be partitioned vertically (separate tables for different concerns) instead of horizontally (split the same table across nodes). Read replicas should only be used when other options have been exhausted. Sharding should only be used when replication has been exhausted.

The practical implication is that replication and sharding are not just infrastructure changes. They are architectural changes. They affect how the application reads and writes data. They affect how transactions are handled. They affect how consistency is ensured. Replication or sharding cannot just be added to infrastructure; the application must be designed to work with them.

This is also why choosing the right database matters. Some databases handle replication and sharding transparently. They abstract the distributed systems complexity and present a simple interface. Other databases require replication and sharding to be managed manually. The replication strategy must be chosen, the sharding key configured, cross-shard queries handled. The trade-off is between control and complexity. Manual management gives control but requires expertise. Transparent management reduces complexity but limits flexibility.

Replication and sharding convert scaling problems into distributed systems problems. Distributed systems problems are harder than scaling problems. The question is whether the scaling problem is complex enough to justify the distributed systems problems that come with it.

← Prev Post Next Post →