user@techdebt:~/blog$_
$ cd ..

The Shard

Key Space: A Z hash(key): 0 2³² Shard Rebalancing
mode:
$ shard inspector
click Step or Play to inspect shard distribution
$ simulation.log

the problem

Vertical scaling has a ceiling. The biggest single server you can buy has finite RAM, finite disk, finite CPU. When your data outgrows it, you have two choices: delete data or split it across machines.

Splitting is sharding (also called partitioning). Each machine holds a subset of the data. The hard part: deciding which data goes where, and what happens when you need to change that decision.

range sharding

The simplest approach: divide the key space into contiguous ranges. Users A-F go to shard 1, G-M to shard 2, and so on.

def find_shard(key):
    if key[0] <= 'F':   return shard_1
    elif key[0] <= 'M': return shard_2
    elif key[0] <= 'S': return shard_3
    else:                return shard_4

Range sharding preserves order: a range query like “all users from A to D” hits only one shard. But real data isn’t uniformly distributed. Names cluster around certain letters. Products starting with “iPhone” all land on the same shard. One shard gets hammered while others sit idle.

hash sharding

Hash the key, then assign based on the hash value. Keys that were neighbors in the alphabet end up on different shards.

def find_shard(key, num_shards):
    h = hash(key)
    shard_size = MAX_HASH // num_shards
    return h // shard_size

Hash sharding distributes data evenly regardless of key patterns. The same names that clustered on one range shard now spread across all shards.

The tradeoff: you’ve destroyed ordering. A range query “all users A-D” must now check every shard, a scatter-gather operation. Each shard scans its local data, the coordinator merges results. Latency is bounded by the slowest shard.

the hotspot problem

Even hash sharding can’t prevent all hotspots. If one key is extremely popular (a viral tweet, a celebrity’s profile), all reads for that key hit the same shard. The key is evenly distributed, but the load isn’t.

Mitigations:

  • Key splitting: Append a random suffix (0-9) to hot keys. Reads scatter across 10 shards and merge. Instagram uses this for celebrity accounts.
  • Caching: Put a cache layer in front of hot shards.
  • Read replicas: Replicate the hot shard for read scaling.

rebalancing

Your cluster needs a new shard. Now what? You have to move data: some items from existing shards to the new one. This is rebalancing.

The naive approach: rehash everything with the new shard count. Like hash(key) % N changing when N changes, most data moves. Terrible.

Better approaches:

  • Fixed partitions: Pre-split your data into many more partitions than shards (e.g., 1000 partitions across 4 shards). Adding a shard just reassigns whole partitions; no item-level scanning.
  • Dynamic splitting: When a partition gets too large, split it in two. When too small, merge neighbors. DynamoDB does this automatically.

where it shows up

  • MongoDB: Range or hash sharding. Chunks (64 MB default) are the unit of migration. The balancer moves chunks between shards automatically.
  • Vitess: MySQL sharding layer built at YouTube. Supports range and hash-based sharding with automatic resharding.
  • CockroachDB: Range-based with automatic splitting and rebalancing. Ranges are 512 MB by default.
  • DynamoDB: Hash-based with automatic partition splitting. Partition keys determine distribution. Hot partitions are split adaptively.