InterviewKata
System design · Module 15 of 29 · Level 3: Data

Sharding and consistent hashing

Replication copies all the data to more machines; it doesn't help when the data, or the writes, are too much for one machine. Sharding splits the data so each machine holds and handles only part of it. It's the main way to scale writes and storage, and also the decision hardest to undo, because the way you split shapes every query afterwards.

New to this?Start with B5 · Data structures behind systems in Level 0; this module assumes it.

1. The idea

A city library outgrows its building, so it opens branches. Books by authors A–F go to the north branch, G–M to the east, and so on. Each branch is smaller and busier only with its share. But "every book published in 2026" now means visiting every branch, and if one author becomes wildly popular, their branch is swamped while others sit quiet.

Three ways to map keys to shards

StrategyHowGoodBad
Hashshard = hash(key), mapped onto shards (ideally with consistent hashing)Even spread; the usual defaultRange queries across keys hit every shard
RangeKey ranges per shard: users A–F, or IDs 1–1MEfficient range scans; easy to reason aboutHot spots when traffic clusters (all new rows at the end of the range)
DirectoryA lookup table maps each key (or tenant) to a shardFully flexible; can move one big customer to its own shardEvery request needs a lookup; the directory must be fast and highly available

2. How it works

Three ways to decide where a row lives

Every sharded system needs a rule that turns a key into a shard. There are three basic rules, and each one makes a different kind of query cheap. Take a bookshop marketplace with three shards for its orders, and switch between the rules:

Many real systems combine them: Cassandra and DynamoDB hash the partition key to pick a node, then keep rows inside each partition sorted so "this customer's orders, newest first" is one ordered read. The table in Under the hood lists which databases use which rule.

Sharding vs federation

Sharding splits one kind of data by rows: all orders, spread across several databases. Federation (also called functional or vertical partitioning) splits by feature: customers in one database, orders in another, the catalogue in a third. It's usually the first split a growing product makes, because it's simple: each database is smaller, busy with one kind of work, and can be tuned and scaled on its own.

Why hash(key) % N is a trap

The simplest hash sharding is "hash the key, take the remainder by the number of shards". It spreads keys evenly, until you add a shard. Press Play.

Consistent hashing

Picture the hash values arranged in a circle (in practice 0 to 232 − 1). Each node is hashed to a point on the ring; each key is hashed too, and belongs to the first node clockwise from it.

Try both in section 3.

Hot spots: keys spread evenly, traffic doesn't

Hashing evens out keys, not requests. A celebrity's account or a viral post lives on one shard and can overload it. Fixes, from simplest:

What gets harder

3. Try it yourself

Hash ring simulator

10,000 keys on a ring. Add or remove a node and see how many keys move, then raise the virtual nodes and watch the load even out.

1

Pick the shard key

4. Trade-offs

Shard late, but plan early

One big database with replicas carries most products for years (Module 02's numbers). Shard when the math says so, but pick IDs and data boundaries early that make sharding possible later.

Locality vs spread

Sharding by user keeps a user's data together (fast, transactional) but a power user becomes a hot shard. Sharding by something finer spreads load but scatters queries.

Many small shards

Starting with many logical shards (hundreds), mapped onto a few machines, lets you rebalance by moving whole shards instead of splitting them later.

Buy it rather than build it

DynamoDB, Cassandra, MongoDB, Spanner, CockroachDB and Vitess or Citus (for MySQL and PostgreSQL) shard and rebalance for you. Your job is still the key.

5. In real systems

The technologies up close

The default answer in an interviewShard only after showing the numbers need it, then four lines: the key (user_id, because every main query is per user), the strategy (hash, with consistent hashing), the cost (global trending needs a scatter-gather, so I precompute it), and the hot-key plan. And don't build it yourself: DynamoDB with user_id as partition key, or Vitess/Citus for SQL, is a complete answer.

Hash sharding with consistent hashing

The default · once the numbers demand it

Hash the shard key and map it onto shards with a ring (or fixed hash slots), so keys spread evenly and adding a shard moves only a fraction of them.

How it works

Covered above. Choosing the key is the real decision: high cardinality, even load, and common queries hit one shard. Good: user_id for user-centric apps, order_id for orders. Bad: a boolean like is_premium (two shards), created_at on a growing table (every new write hits the newest shard), country (skewed). The key is close to permanent.

Status today

How Cassandra, DynamoDB, Redis Cluster, Kafka and most distributed caches place data. Failures are handled by replicas, not by moving data; data moves only for planned changes.

Use it when

  • One database can't keep up with storage, writes or reads, and you've shown it with numbers
  • Most queries are scoped to one entity (a user, a tenant, a channel)

In the interview

Four steps: The limit is 50k writes a second at peak. Queries are per user, so shard key user_id. Hash with consistent hashing. Global trending needs every shard, so I precompute it in a background job. Add the growth plan: start with 64 shards; consistent hashing moves only a fraction when we add more.

🚩 Red flags
  • Sharding on day one. Premature sharding is the most common mistake interviewers see.
  • Naming a shard key without checking the queries: if the main page hits every shard, the key is wrong.
  • Confusing the two problems: virtual nodes fix uneven keys; replication, caching and salting fix uneven traffic.

Range sharding

Situational · range scans, tenants

Each shard owns a contiguous range of keys: IDs 1–1M on shard 1, or one range per customer.

How it works

Range scans stay on one shard and data is easy to reason about. But traffic clusters: with time or sequential IDs, all new writes land on the last range. Systems that use ranges (HBase, Bigtable, CockroachDB, MongoDB's ranged sharding) split hot ranges automatically.

Status today

Common inside databases that auto-split; less common as a hand-built scheme.

Use it when

  • Multi-tenant SaaS (a range or shard per customer)
  • Archives and analytics partitioned by time

In the interview

Partition the archive by month for cheap range scans and drops; live writes stay hash-sharded by user.

🚩 Red flags
  • Time-range sharding for write-heavy data. The newest shard melts while the rest idle.

Directory sharding

Rare · flexibility at a cost

A lookup table maps each key or tenant to a shard.

How it works

Every request first asks the directory (usually cached), then goes to the shard. Any tenant can be moved by changing one row, so a huge customer can get a shard of its own.

Status today

Used for large multi-tenant products; rarely the interview answer.

Use it when

  • A few tenants are vastly bigger than the rest and need isolating

In the interview

Only for tenant isolation: A directory maps each tenant to a shard, so the biggest customers get their own.

🚩 Red flags
  • Forgetting the directory is on every request's path: it must be cached and highly available.

Vertical partitioning

Situational · different access patterns

Split data by how it's used, each part in a store tuned for it, rather than splitting rows.

How it works

For posts: post_content (written once, read many → a B-tree database), post_metrics (counters updated constantly → an in-memory counter store), post_analytics (append-only events → a time-series or columnar store). Inside one database, the same idea moves rarely used or large columns out of a hot table.

Status today

Standard practice in large products, often combined with horizontal sharding.

Use it when

  • One entity has parts with very different read/write patterns
  • A hot table is slowed by big, rarely read columns

In the interview

Like counts change thousands of times a second, so they live in Redis counters flushed periodically; the post itself stays in PostgreSQL.

🚩 Red flags
  • Sharding everything when only one part (the counters) is write-hot.

Two-phase commit (2PC)

Rare · usually the wrong answer

A protocol to commit one transaction across several databases: a coordinator asks everyone to prepare, then tells them all to commit.

How it works

Participants hold locks between prepare and commit. If the coordinator dies after prepare, participants are stuck holding those locks until it recovers. Every transaction pays extra round trips.

Status today

Exists (XA transactions), and is used inside some distributed databases (Spanner runs 2PC over consensus groups, so the coordinator can't simply vanish). Across services and third-party APIs it's impractical: payment providers don't take part in your transactions.

Use it when

  • Inside a database that does it for you (distributed SQL)

In the interview

A user's data stays on one shard so transactions are local; across services we use a saga with compensations rather than 2PC.

🚩 Red flags
  • Proposing 2PC across microservices. Expect: what happens when the coordinator crashes after prepare?
  • Needing cross-shard transactions often: a sign the shard key or service boundaries are wrong.

Three-phase commit (3PC)

Rare · know why nobody uses it

A variant of two-phase commit that adds a "pre-commit" phase so participants can finish without a crashed coordinator.

How it works

Phases: can-commit, pre-commit, do-commit. If the coordinator dies after pre-commit, participants can time out and commit on their own. But it assumes bounded network delays: under a network partition, two groups can decide differently. Real systems instead run 2PC over consensus-replicated participants (Spanner) or use sagas.

Status today

A textbook protocol, rarely deployed.

Use it when

  • Answering "how would you fix 2PC's blocking?" in a theory question

In the interview

3PC removes blocking only if delays are bounded, which networks don't guarantee; I'd replicate the coordinator with consensus, or use a saga.

🚩 Red flags
  • Proposing 3PC as the fix for distributed transactions in a product design.

Sharded counters

Situational · hot counts

One logical counter (likes on a viral post, views of a video) stored as N sub-counters, so writes spread out and reads add them up.

How it works

Each increment goes to a random sub-counter (likes:42#0 … #15) on different shards (the scene in section 5). Reads sum all N, usually cached for a few seconds. Choose N from the peak write rate divided by what one shard handles; start small and add sub-counters only to hot items. Combine with batching (Module 18) to collapse many increments into one write.

Status today

A well-known pattern (Google's App Engine documentation popularised the name); Twitter, Instagram and YouTube-style counts work this way, often with approximate, eventually consistent totals.

Use it when

  • Likes, views, votes, inventory counters under heavy write load

In the interview

Like counts are sharded counters, 16 sub-counters for hot posts, summed and cached for 5 seconds.

🚩 Red flags
  • One row per post updated 100,000 times a second.
  • Sharding every counter, including the millions that get one like an hour.

6. Remember it

Memory hook: "Split by how you ask; ring it so growth moves little"Choose the shard key from your most common query, and place shards on a hash ring (or slots) so adding capacity moves only a small slice.
How this hook works: what "Split by how you ask; ring it so growth moves little" means

What the slogan means, phrase by phrase

"Split by how you ask"
The shard key comes from the dominant access pattern: if every request is about one user, shard by user so each request touches one shard.
"ring it"
Map keys with consistent hashing (or hash slots), not with % N.
"so growth moves little"
Adding a node moves about 1/N of the keys instead of nearly all of them; virtual nodes spread that movement and the load evenly.

Why this picture fits: a library splits branches by how people look books up, and opening a new branch should only move the books it takes over, not reshelve the whole city.

The story → the concept

Library branchesSharding
BranchesShards (separate databases)
"Authors A–F go north"The shard key and mapping
A famous author's swamped branchA hot spot
"Every book from 2026" across all branchesA scatter-gather query
Opening a new branchAdding a node: consistent hashing moves only its share
Each branch covering several small areasVirtual nodes

Watch it on an example

  1. An orders table reaches 40 TB and 30,000 writes a second: one database can't keep up.
  2. Most queries are "this customer's orders", so shard by customer_id, hashed onto 256 logical shards on 8 machines.
  3. Black Friday doubles traffic: add 8 machines and move 128 logical shards, about half the data, in the background, with no key remapping.
  4. The finance team's "all orders yesterday" report runs on the warehouse, not across shards.

The trap it saves you from

Sharding by a key that matches no query (or a skewed one, like country), and then paying for scatter-gather and hot shards on every request.

It still works when…

You assign WebSocket connections or cache entries to servers (Modules 08, 19): the same ring sends each user or key to a predictable server, and resizing the pool disturbs only a slice.

7. Common mistakes

Sharding before it's neededSharding adds routing, rebalancing, cross-shard queries and harder transactions. Do the math first; replicas, indexes and caching often suffice.
Low-cardinality or skewed keyscountry, status or is_premium give a few huge shards. Use high-cardinality keys like user or order IDs.
Time as the only key for hot writesSharding by date sends all of today's writes to one shard. Combine with a hash (hash(device_id) + day) or use time only for archives.
Using % N mappingChanging N remaps almost everything. Use consistent hashing, slots, or many logical shards.
Frequent cross-shard transactionsIf many operations span shards, the boundaries are wrong. Re-draw them around what changes together.

8. Design exercise (25 minutes)

  1. Choose keys for: a chat app's messages, a multi-tenant SaaS app's data, a ride-hailing app's trips, a metrics system's time series. Say what each key makes easy and what it makes hard.
  2. Size it: 120,000 writes a second at peak, each shard comfortable at 10,000. How many shards, with headroom? How many logical shards would you start with?
  3. Plan a hot key: one tenant produces 30% of all traffic. Describe two ways to handle it.
  4. Reshard on paper: write the steps to move from 8 to 16 physical machines without downtime.
  5. Write it down: the three properties of a good shard key, and why % N fails.

9. Self-check quiz

With consistent hashing, roughly what fraction of keys move when you add the Nth node?

The new node only takes over the arcs just before its points on the ring.

Almost every query in a social app is per user. Which shard key?

High cardinality, even spread, and each user's queries stay on one shard.

What do virtual nodes fix?

Many points per node make arcs even, and a failed node's load is shared by many others.

One viral post's like counter overloads its shard. A common fix?

More shards don't help one key. Splitting the key spreads its writes; reads add the parts up.

Which is the worst shard key for a busy orders table?

Every new order has the latest timestamp, so all writes land on the newest shard.

A viral post's like counter overloads one shard. What helps most?

Virtual nodes even out keys, not traffic on one key. Salting spreads that key's writes over several shards.

10. Go deeper

ResourceTimeWhy
Designing Data-Intensive Applications, chapter 6 (Partitioning)1.5 hKey-range vs hash partitioning, secondary indexes across partitions, rebalancing and routing.
Redis Cluster specification30 minHash slots, hash tags, redirections and resharding in a real system.
Dynamo paper (sections 4.1–4.3)30 minConsistent hashing with virtual nodes, as first used at scale.
Sharding (Hello Interview)20 minHow to raise sharding in an interview, and the follow-ups to expect.

11. Under the hood (optional)

How data is really split across machines, moved when machines change, and found again. Not needed for most interviews; open it when you want to know how the products really do it. All the deep dives are listed on the Under the hood page.

Partitioning and shardingRange vs hash vs directory, consistent hashing variants with a live lab, splitting, hot keys, routing, resharding, 10 products comparedabout 35 min

Sharding is three decisions: how a key maps to a shard, how shards move when you add machines, and how a request finds the right one. Each product makes them differently, and the consequences show up as hot spots, painful resharding and slow cross-shard queries.

Mapping keys to shards

SchemeHowStrengthWeaknessExamples
RangeContiguous key ranges per shardRange scans stay on one shard; easy to splitSequential keys (timestamps, increasing IDs) all hit the last shardBigtable, HBase, CockroachDB, TiDB, Spanner, MongoDB ranged keys
HashA hash of the key picks the shardEven spread, no hot tailRange queries hit every shardCassandra, DynamoDB, Redis Cluster, Citus, Kafka
Hash then rangeHash the partition key; keep rows inside a partition sorted by a second keySpread plus ordered reads within one entity ("a user's messages by time")One huge or hot partition is still one placeCassandra (partition + clustering key), DynamoDB (partition + sort key)
DirectoryA lookup table says where each key or tenant livesMove any key anywhere; pin big tenantsThe directory must be fast, cached and highly availableVitess lookup vindexes, tenant routing tables, MongoDB zones

Consistent hashing and its relatives

With hash(key) mod N, adding one server changes N, so almost every key moves. Consistent hashing schemes move only about 1/N of the keys. Add a node and compare:

SchemeHow it picksNotesUsed in
Ring with virtual nodesEach server owns many points on a hash ring; a key goes to the next point clockwiseMore virtual nodes give a more even spread; removal and weights are easyCassandra (16 tokens per node by default since 4.0), Dynamo, many caches
Fixed slotsHash to one of a fixed number of slots; a table maps slots to serversRebalancing moves whole slots; the table is small and explicitRedis Cluster (16,384 slots), Couchbase vBuckets, Elasticsearch shards
Rendezvous (highest random weight)Score every server with hash(key, server); take the highestNo ring, perfectly even on average, O(N) per lookupLoad balancers, CDNs choosing caches, client-side sharding
Jump consistent hashA few lines of arithmetic map a key to bucket 0…N−1No memory, very even, but servers must be numbered and only the last can be removedStorage and cache layers with numbered shards (from Google, 2014)
Maglev hashingA fixed-size lookup table filled by each backend's preference orderVery fast lookups, small disruption when backends changeGoogle's Maglev load balancer, Envoy's Maglev policy
Bounded loadsConsistent hashing, but a server above (1 + ε) × average load passes keys onCaps hot servers while keeping most keys in placeHAProxy and some cache tiers

Fixed partitions vs splitting

  • Many fixed partitions from day one (Kafka partitions, Elasticsearch primary shards, Redis slots, Citus's default of 32 shards per table): adding a machine moves whole partitions, never re-hashes keys. The count is hard to change later: Kafka can only add partitions (which remaps keys), Elasticsearch needs split, shrink or reindex.
  • Split as you grow (Bigtable and HBase regions, CockroachDB ranges up to 512 MiB by default, TiDB regions, Spanner splits, MongoDB chunks): a range that gets too big or too busy splits in two, and a balancer moves pieces around. No up-front guess, but each split and move is work the system must do safely online.
  • DynamoDB does both automatically: partitions split when they pass about 10 GB or sustained heat ("split for heat"), and adaptive capacity lends throughput to busy partitions. Each partition still serves at most about 3,000 reads and 1,000 writes per second, so a single hot key can't go faster.

Choosing a shard key, and hot keys

  • High cardinality, even frequency, not monotonic: user or tenant ID, order ID. Not country (few values), not created_at (all new writes on one shard), not status.
  • Matches the main query: the key in the WHERE clause of the busiest request, so it hits one shard.
  • Hot keys (a celebrity, a flash sale item) beat any key choice. Fixes: cache the reads; salt the writes across several sub-keys and merge on read; split a hot range; or give the hot entity its own shard.

Routing, rebalancing and secondary indexes

Who finds the shardHowExamples
Smart clientThe driver caches the shard map and goes direct; redirects (MOVED) correct itRedis Cluster clients, Cassandra token-aware drivers, Kafka producers
Proxy / routerA stateless tier knows the map and forwardsMongoDB mongos, Vitess VTGate, Citus coordinator, Twemproxy
Any nodeWhichever node receives the request coordinates itCassandra coordinators, CockroachDB gateway nodes, Elasticsearch
  • Resharding without downtime follows one recipe: copy existing data to the new layout while streaming ongoing changes (Vitess VReplication, MongoDB's online resharding), verify, then switch reads and writes over, with a way back.
  • Local secondary indexes live on each shard with its data: writes stay single-shard, but a query by that column asks every shard (Elasticsearch, Cassandra SAI, DynamoDB local secondary indexes). Global secondary indexes are partitioned by the indexed column: one-shard reads, but every write updates another shard, usually asynchronously (DynamoDB GSIs, CockroachDB indexes).
  • Scatter-gather is as slow as the slowest shard: with 50 shards, a 1-in-100 slow response hits about 40% of queries. Co-locate data that's queried together (Citus co-location, Spanner interleaved tables) and keep cross-shard queries off the hot path.

Product by product

ProductHow a key finds its shardSplitting and rebalancingThe detail worth knowing
MongoDBRanged or hashed shard key; mongos routers consult config serversA balancer moves chunks; collections can be resharded online (since 5.0)An increasing ranged key sends every insert to one shard
CassandraMurmur3 hash of the partition key onto a token ring with virtual nodesA new node takes over token ranges and streams their dataNo cross-partition queries by design: one table per query
DynamoDBHash of the partition keyAutomatic splits by size and heat; adaptive capacityAbout 1,000 writes and 3,000 reads per second per partition
Vitess (MySQL)Vindexes map a column to a keyspace ID; shards own ranges of IDsCopy with VReplication, then switch trafficGrows an unmodified MySQL application past one server
Citus (PostgreSQL)Hash of a distribution column into a fixed number of shardsShards move between worker nodesCo-located tables join locally; small tables are copied to every node
CockroachDB / TiDBKey ranges, each its own Raft groupAutomatic splits by size and load, then rebalancingSequential keys make a hot range; hash-sharded indexes spread them
SpannerKey ranges (splits), each a Paxos groupAutomaticInterleaved tables keep a parent with its children
Elasticsearch / OpenSearchHash of the routing value modulo the primary shard countFixed per index; split, shrink, reindex or roll over time-based indexesToo many small shards waste memory; too few cap parallelism
Redis ClusterCRC16 of the key modulo 16,384 slotsSlots migrate; clients follow MOVED and ASKHash tags keep related keys in one slot
KafkaHash of the message key modulo the partition countPartitions can be added, never split; adding remaps keysOrder holds only within a partition

Say it in the interview

Answers that show depth
  • "What happens when you add a shard?" With modulo hashing nearly everything moves; with consistent hashing or fixed slots about 1/N moves, copied in the background while the old owner keeps serving, then switched over.
  • "Your shard key is a timestamp. Problem?" Every new write goes to the newest range: one hot shard. Hash it, prefix it with a high-cardinality value, or use a hash-sharded index.
  • "How would you handle a celebrity's like counter?" Salt the key into, say, 16 sub-counters on different shards, increment one at random, sum on read (and cache the sum).

With consistent hashing and N servers, roughly what fraction of keys move when one server is added?

Only the keys the new server takes over. With modulo hashing, about N/(N+1) of keys move.

Why are queries on a local secondary index expensive in a sharded store?

The matching rows could be anywhere: scatter-gather.

DynamoDB splits hot partitions automatically. Why can a single hot key still throttle?

A split can't divide one key. Salt it or cache it.

Sources