dissected.io
Lesson 13 of 29
Intermediateintermediateshardingdatabasesscalingdistributed-systems12 min read

Database Sharding

A single database can only handle so many reads and writes per second. When you hit that ceiling, sharding lets you split your data horizontally across multiple database instances — each owning a subset of the data.

Database Sharding — Router & Shards

userId: —

Router

hash(userId) → shard

Shard 1

users 0–999

idle

Shard 2

users 1000–1999

idle

Shard 3

users 2000+

idle

What Sharding Is

Sharding splits your database horizontally across multiple machines. Each machine is called a shard, and each shard holds a distinct slice of your data. No two shards hold the same rows — together, they hold everything.

A router or coordinator sits in front of the shards. When a query comes in, the router decides which shard owns the relevant data and forwards the request there. From the application's perspective, it talks to one database — the routing is transparent.

Think of it like a library splitting its books across multiple buildings. Each building holds a section of the collection. A front desk (the router) tells you which building has the book you need.

The Shard Key

The shard key is the column or field you use to decide which shard a piece of data belongs to. It's the most important decision you'll make when designing a sharded system — get it wrong and you'll regret it.

A bad shard key causes hotspots: one shard gets hammered while others sit idle. A good shard key distributes data evenly and makes routing predictable. High cardinality fields — user ID, order ID, phone number — tend to make better shard keys than low cardinality ones like country or status.

Sharding Strategies

There are three main approaches to deciding which shard a record goes to.

Range-based sharding divides the key space into contiguous ranges. Simple to understand, but can be uneven if some ranges see more traffic than others.

# Range-based: split users alphabetically
Shard 1: user_name A–M
Shard 2: user_name N–Z

router.get_shard(user_name):
  if user_name <= "M":
    return shard_1
  else:
    return shard_2

Hash-based sharding runs the key through a hash function and uses the result to pick a shard. Distributes data evenly by default, but you lose the ability to do range queries efficiently.

# Hash-based: distribute by user ID
shard_index = hash(user_id) % num_shards

router.get_shard(user_id):
  return shards[hash(user_id) % len(shards)]

Directory-based sharding maintains an explicit lookup table mapping keys to shards. Highly flexible — you can assign any key to any shard and rebalance without changing the hash function. The tradeoff is that the lookup table becomes a bottleneck and a single point of failure.

# Directory-based: lookup table
shard_map = {
  "user_001": "shard_3",
  "user_002": "shard_1",
  "user_003": "shard_2",
  ...
}

router.get_shard(user_id):
  return shard_map[user_id]

The Hotspot Problem

Even with a good sharding strategy, some shards can still get disproportionate traffic. The classic example is the celebrity problem: a famous user has millions of followers, so every action they take — posting, updating their profile — generates a flood of reads and writes to whichever shard holds their data. Normal users generate 10 requests per minute; a celebrity might generate 10,000.

Hash-based sharding helps spread data evenly, but it doesn't protect you from a single key being accessed far more than others. Common mitigations include consistent hashing (which makes rebalancing less disruptive), adding a random suffix to hot keys to spread them across multiple shards, or treating hot keys as a special case with dedicated capacity.

The Problems Sharding Introduces

Sharding solves the single-machine capacity problem but introduces a different set of headaches.

Cross-shard queries are painful. A JOIN across two tables that live on different shards requires querying both shards and merging results in your application layer. What was a single SQL query becomes coordination logic you now own.

Rebalancing is disruptive. When you add a new shard, you need to redistribute data across the new set of shards. With hash-based sharding, changing num_shards invalidates the entire mapping — nearly every record needs to move. Consistent hashing mitigates this.

Distributed transactions across shards are complex. If an operation touches data on two different shards, you need a two-phase commit or equivalent to keep things consistent. Most teams avoid cross-shard transactions by design.

Operational complexity compounds everything. You're now running and monitoring N databases instead of one. Backups, schema migrations, monitoring — all multiplied.

When To Shard

Sharding should be a last resort, not a first instinct. Before you shard, exhaust every other option:

  • Vertical scaling: buy a bigger machine — more RAM, faster disks, more CPU cores
  • Read replicas: offload read traffic to replica nodes (covered in the next lesson)
  • Caching: serve frequent reads from Redis or Memcached, never hitting the database
  • Query optimisation: add indexes, rewrite slow queries, review your schema

Most applications never need to shard. The ones that do typically hit write throughput limits that neither read replicas nor caching can address.

When sharding does make sense, real systems tend to choose their shard key around their primary access pattern. Instagram shards by user ID — most queries are "give me everything for this user." Uber shards by geographic region (city) because ride data is inherently local. WhatsApp shards by phone number hash because message lookups are always user-scoped.

Key Takeaways

  • Sharding splits data horizontally across multiple database machines, each holding a distinct subset
  • The shard key determines which shard owns each record — it's the single most important sharding decision
  • Hash-based sharding gives even distribution; range-based gives query flexibility; directory-based gives maximum control at the cost of a lookup bottleneck
  • The hotspot problem means even good shard keys can leave one shard overwhelmed by a single high-traffic entity
  • Cross-shard joins, rebalancing, and distributed transactions are the main pain points sharding introduces
  • Shard only after vertical scaling, read replicas, caching, and query optimisation have been exhausted

Next up: Replication and Consistency — how keeping copies of your data across multiple nodes gives you fault tolerance and read performance, and the consistency trade-offs that come with it.

Enjoyed this breakdown?

Get new lessons in your inbox.