Sharding: Splitting the Data Itself
Eventually one database server runs out of room — too many rows, too many writes, too much for a single machine to hold. Sharding (horizontal partitioning) splits one logical dataset across many database nodes, so each shard owns only a subset of the rows. Capacity now scales by adding machines instead of buying a bigger one. The catch: the moment your data lives on more than one node, every decision — which key to split on, how to route a query, how to rebalance — gets harder.
Vertical Scaling vs Horizontal Partitioning
Scaling up means giving one server more CPU, RAM, and disk. Simple, but there is a hard ceiling — and the biggest machine is expensive and still a single point of failure. Sharding scales out: it slices the table by row and hands each slice to a different node. Ten nodes hold roughly a tenth of the data and take a tenth of the write load each.
One table, split by row
Single DB (users: 400M rows) — one machine, maxed out Sharded across 4 nodes by user_id: shard 0 → users 0M – 100M shard 1 → users 100M – 200M shard 2 → users 200M – 300M shard 3 → users 300M – 400M Each node stores 1/4 of the rows and serves 1/4 of the writes.
The Core Question
Is your data or write volume genuinely too large for one machine? If a single well-tuned server (with replicas and caching) still copes, don't shard yet — it adds permanent operational complexity. Shard when the data won't fit, the write throughput exceeds one node, or working sets blow past memory.
Sharding Strategies
Everything comes down to one function: given a row, which shard does it live on? There are four common answers, each with different trade-offs.
Range-based
Split the key space into contiguous ranges (A–F, G–M, …). Great for range scans and easy to reason about — but skewed data or monotonically increasing keys (timestamps, auto-increment IDs) dump everything onto the last shard: an instant hot spot.
Hash-based
Route by hash(key) % N. Spreads rows evenly and kills hot spots from sequential keys. The downside: range queries are scattered across every shard, and changing N reshuffles almost everything (unless you use consistent hashing).
Directory / lookup-based
Keep an explicit lookup table mapping each key (or key range) to a shard. Maximum flexibility — you can move any key anywhere and rebalance surgically — but the directory is a new dependency and a potential single point of failure and bottleneck.
Geo / entity-based
Shard by a natural boundary — region for locality/latency, or tenant/customer so all of one entity's data sits together (keeping most queries single-shard). Risk: uneven entities (one whale tenant) create their own hot shard.
Resharding & Consistent Hashing
Plain hash(key) % N has a nasty property: change N and almost every key remaps to a different shard. Adding a single node can trigger a full data reshuffle across the fleet.
The modulo problem
4 shards → hash(k) % 4 Add one node → hash(k) % 5 key 1001: %4 = 1 → %5 = 1 (stayed) key 1002: %4 = 2 → %5 = 2 (stayed) key 1003: %4 = 3 → %5 = 3 (stayed) key 1004: %4 = 0 → %5 = 4 (MOVED) In practice ~80% of keys move — a massive, disruptive migration.
Consistent hashing
Place both keys and nodes on a hash ring; a key belongs to the next node clockwise. Add or remove a node and only the keys in its arc move — on average 1/N of the data instead of nearly all of it. Virtual nodes (many points per physical node) smooth out the distribution. This is how Cassandra, DynamoDB, and Riak keep resharding cheap when the cluster changes size.
Rebalancing is still painful
Even with consistent hashing, moving data means copying gigabytes between live nodes while serving traffic, keeping source and destination in sync during the cutover, and updating routing without dropping requests. Plan sharding — and headroom — before you desperately need it.
Cross-Shard Queries & Replication
Sharding is fast when a query touches one shard. It gets expensive the moment a query needs many.
Scatter-gather & joins
A query without the shard key must fan out to every shard (scatter), then merge the results (gather) — as slow as the slowest node. Joins across shards and transactions spanning shards are worse: they need distributed coordination (two-phase commit / sagas), which is slow and failure-prone.
SELECT * FROM orders WHERE user_id = 42; // 1 shard — fast SELECT * FROM orders WHERE status = 'refunded'; // no shard key → ask all N shards, merge → slow JOIN orders × users across shards // data on different nodes → distributed join, avoid if you can
Sharding ≠ Replication (they compose)
Sharding splits different data across nodes to scale capacity and write throughput. Replication copies the same data onto multiple nodes for read scaling and fault tolerance. They are orthogonal and used together: shard for size, then replicate each shard so no single node is a point of failure. A shard with no replicas means losing that node loses that slice of your data.
Live Shard Distribution
Sharding Pitfalls
- →Bad shard key: a low-cardinality or skewed key (country, status, created_at) guarantees hot shards. This is the mistake that's hardest to undo — choose deliberately.
- →Hot spots: even a good scheme can concentrate load on a celebrity user or whale tenant. Watch per-shard load and plan to split or isolate the outlier.
- →Cross-shard transactions: distributed commits across shards are slow and fragile. Design so a transaction stays within one shard whenever possible.
- →Premature sharding: sharding adds permanent complexity. Exhaust vertical scaling, read replicas, and caching before splitting your data across nodes.
- →Plain modulo hashing: hash(key) % N remaps almost everything when N changes. Use consistent hashing so adding a node only moves ~1/N of the data.
- →Forgetting replication: a shard without replicas is a single point of failure. Losing that node loses a slice of the dataset — shard and replicate together.
Key Takeaways
- →Sharding is horizontal partitioning: split one dataset across many nodes so each holds a subset, scaling capacity and write throughput beyond a single machine.
- →Only shard when the data or write volume truly exceeds one node — after vertical scaling, replicas, and caching. It adds lasting operational complexity.
- →Pick from range, hash, directory/lookup, or geo/entity strategies — each trades even distribution against range-query and locality needs.
- →The shard key is the most important, least reversible decision: high cardinality, even spread, and single-shard queries.
- →Bad or skewed keys create hot shards — a fix needs a better key, not just more nodes.
- →Use consistent hashing (with virtual nodes) so adding or removing a node moves only ~1/N of the data instead of nearly all of it.
- →Cross-shard queries, joins, and transactions are expensive; keep hot-path queries on a single shard.
- →Sharding and replication are different and complementary — shard for size, replicate each shard for reads and fault tolerance.
Quick Reference: Sharding Strategies
Range-based
Contiguous key ranges per shard. Excellent for range scans, simple to reason about. Weak to skew and sequential keys → hot shards.
Hash-based
hash(key) % N (ideally consistent hashing). Even distribution, no sequential hot spots. Range queries scatter across all shards.
Directory / lookup
Explicit key→shard map. Maximum flexibility and surgical rebalancing, at the cost of a new dependency and possible bottleneck.
Geo / entity-based
Partition by region or tenant/customer for locality and single-shard queries. Uneven entities (a whale tenant) can still create a hot shard.