Horizontal sharding and consistent hashing
Range vs hash partitioning, shard routers, cross-shard joins, two-phase commits, and distributed IDs.
What you will learn
- Compare range, modulo-hash and consistent-hash sharding, including what moves when a shard is added.
- Choose a shard key by cardinality, write spread and query co-location.
- Handle queries without the shard key, transactions across shards, and IDs that are unique across shards.
Horizontal database sharding
Horizontal sharding partitions a single logical table across multiple independent physical database instances (shards) where each shard holds a mutually exclusive subset of rows. As with federation, each shard sees less traffic, has less to replicate, keeps more of its (smaller) data and indexes in memory, and accepts writes in parallel with the others. If one shard goes down the others keep working, but you still need replication within each shard to avoid losing its data.
In the ideal case, where load spreads evenly across shards:
In practice distribution can become lopsided: a set of power users on one shard can load it far more than the others.
1. Partitioning strategies
A. Range-based sharding
- Mechanism: Rows are assigned to shards based on contiguous key ranges (e.g. Shard 1:
user_id1–1,000,000; Shard 2:user_id1,000,001–2,000,000; or by creation dates). - Pros: Efficient range scans (
WHERE created_at BETWEEN '2026-01-01' AND '2026-03-31'). - Cons: High risk of hotspotting — all new writes hit the highest range shard while older shards sit idle.
B. Hash-based (modulo) sharding
- Mechanism: .
- Pros: Uniform distribution of writes across all shards.
- Cons: Resharding is expensive. Going from to shards changes the result of for about of all keys (80% when going from 4 to 5 shards), forcing a cluster-wide data migration.
C. Consistent hashing with virtual nodes
- Mechanism: Keys and shards are hashed onto the same circular hash space (a ring); each key belongs to the first shard found moving clockwise from it. The arrival or departure of a shard only affects its immediate neighbors on the ring.
- Virtual nodes: Placing each shard at a single point spreads keys unevenly, so systems such as Amazon's Dynamo give each physical node many points ("virtual nodes"). This evens out the load and spreads a new node's intake across many existing nodes.
- Rebalancing: Adding a shard moves only about of the keys (the new shard's fair share), and all of them move onto the new shard. Try it in the visual below.
2. Shard key selection criteria
Choosing the wrong shard key is the most expensive mistake in distributed database architecture:
- High Cardinality: The key must have millions of distinct values (e.g.
user_idoraccount_id, nevercountry_codeorstatus). - Uniform Write Distribution: Avoid monotonically increasing sequences as primary keys on range-sharded systems.
- Query Co-location: Queries should target a single shard via the shard key. If 90% of queries filter by
account_id, sharding byaccount_idensures single-shard routing.
3. Distributed joins and cross-shard queries
When a query cannot specify the shard key (e.g. SELECT * FROM orders WHERE status = 'PENDING'), the shard router must execute a Scatter-Gather (Fan-Out):
- Broadcast the query in parallel to all physical shards.
- Collect result streams.
- Merge, re-sort, and paginate the results in the router's memory.
A scatter-gather query is only as fast as its slowest shard (chapter 1's tail-latency effect): if each of shards is independently slow with probability ,
which grows quickly with . Joining data across shards is likewise more complex, and the application logic must know how to work with shards.
4. Distributed transaction coordination
| Pattern | Mechanism | Pros | Cons |
|---|---|---|---|
| Two-phase commit (2PC) | A coordinator asks every participant to prepare, and commits only if all vote yes; otherwise all abort. | All participants commit or none do (atomic commit) | Participants hold locks while they wait for the decision, and the protocol blocks if the coordinator fails |
| Saga | A sequence of local transactions, coordinated by events (choreography) or by an orchestrator, with compensating transactions to undo completed steps if a later one fails. | No distributed locks; each step commits on its own | Compensations must be written by hand, and there is no isolation: concurrent sagas can see each other's partial work, so anomalies need countermeasures |
5. Globally unique distributed ID generation
Auto-increment primary keys cannot be shared across independent database shards. Common alternatives:
- Twitter Snowflake (64-bit integer): IDs are roughly time-ordered ("k-sorted"):That is 63 bits, so every ID fits in a 64-bit integer.
- UUIDv7 (128-bit, RFC 9562): A 48-bit Unix millisecond timestamp followed by random bits. Because successive values sort together, inserts land near each other in a B-tree index; random UUIDs (v4) scatter inserts across the index, which can hurt performance dramatically.
Sharding and routing a key
user_9482 ➔ Shard DB-21/(N+1) of keys move, and only onto the new shard.- App Servers (60k RPS): 1 × Clients & Users
- Shard Router (Vitess): 4 × Application Load Balancer
- Shard 0 (Keys 00-3F): 2 × Primary Relational DB
- Shard 1 (Keys 40-7F): 2 × Primary Relational DB
- Shard 2 (Keys 80-BF): 2 × Primary Relational DB
- Shard 3 (Keys C0-FF): 2 × Primary Relational DB
- Redis Shard Key Cache: 2 × Redis In-Memory Cache
- App Servers (60k RPS) sends requests to Shard Router (Vitess)
- Shard Router (Vitess) sends requests to Shard 0 (Keys 00-3F)
- Shard Router (Vitess) sends requests to Shard 1 (Keys 40-7F)
- Shard Router (Vitess) sends requests to Shard 2 (Keys 80-BF)
- Shard Router (Vitess) sends requests to Shard 3 (Keys C0-FF)
- Shard Router (Vitess) sends requests to Redis Shard Key Cache
Key takeaways
- Range sharding keeps range scans cheap but sends new writes to one hot shard; modulo hashing spreads writes but moves about of the keys when a shard is added.
- Consistent hashing with virtual nodes moves only the new shard's fair share, about .
- A query without the shard key scatters to every shard and is as slow as the slowest: .
- Cross-shard writes need two-phase commit (locks, blocking) or a saga (compensations, no isolation); IDs come from Snowflake or UUIDv7, not auto-increment.
Sources
- System Design Primer: Sharding
- DeCandia et al., "Dynamo: Amazon's Highly Available Key-value Store" (consistent hashing, virtual nodes)
- Jim Gray and Leslie Lamport, "Consensus on Transaction Commit" (2PC blocks if the coordinator fails)
- Chris Richardson, microservices.io: Pattern: Saga
- Twitter Snowflake (2010 README)
- RFC 9562: Universally Unique IDentifiers (UUIDv7)
Adapted from The System Design Primer by Donne Martin, licensed under CC BY 4.0. Changes were made.