Blueprint
Fundamentals

Sharding & partitioning

The scaling move of last resort: split the data across machines and gain unlimited write headroom — while losing joins, transactions, and your ability to change your mind cheaply.

01

What it is

Sharding splits one logical dataset across multiple database servers, each holding a subset of rows, because a single node can no longer handle the write throughput or the storage. It's horizontal partitioning taken across machines — as opposed to vertical partitioning, which splits by columns or tables. Each shard is usually itself replicated, so sharding and replication compose rather than compete.

Interviewers treat sharding as the standard deep dive once your capacity math exceeds one box — past roughly 5k–10k writes/s or a few TB on one node. The scoring is almost entirely on the shard key: whether you chose it from the dominant access pattern, whether you saw the hot-shard failure mode, and whether you know what you gave up. Sharding before the math demands it is itself a scored mistake.

Say this out loud

The math says 30k writes a second, so one Postgres box is out — I'll shard by user_id with consistent hashing, which makes every user-scoped query single-shard, and I'll accept that global queries now fan out.

02

The picture

Writes Routerhash(user_id) % 3 shard 0users 0–33% shard 1users 34–66% shard 2users 67–99%
the router hashes the key and each row lands on exactly one shard — watch the dots split. no shard ever sees the full table again, which is the point and the price.
03

The variants and when to pick each

Hash-based — even spread, dead range queries

Route each row by hash(key) — via consistent hashing or a fixed set of logical partitions, never naive mod N (adding a node under mod-N remaps nearly every key). Distribution comes out even, and hot spots from key locality disappear.

The price is that adjacent keys land on different shards, so range queries become scatter-gather across every node. The production-grade refinement is fixed logical partitions mapped many-to-one onto physical nodes — Redis Cluster's 16384 slots — so rebalancing moves whole slots instead of rehashing keys.

Trade-offEven load in exchange for range scans — if the product needs "messages between Monday and Friday" cheaply, hashing on message ID was the wrong call.

Range-based — great scans, hot tails

Each shard owns a contiguous key range, so range queries hit one or two shards — the natural fit for time-series and anything scanned in key order.

The classic failure is the hot tail: if keys are time-ordered, every write lands on the newest shard while the rest idle. Fixes are compound keys (tenant first, time second) or salting the head of the key — both of which mean designing the key around the write distribution, not only the read pattern.

Trade-offCheap range scans bought at the risk of one shard absorbing all current writes — range-shard on time only if something higher-cardinality leads the key.

Directory-based and geo — flexibility with a dependency

A directory service maps key → shard explicitly. Maximum flexibility: move one noisy tenant to its own shard, rebalance without rehashing, place data wherever you like. The cost is a lookup on every request and a directory that becomes a critical dependency — it gets cached aggressively and replicated, or it becomes the outage.

Geo-sharding partitions by region — EU users on EU shards — which buys data locality and GDPR compliance, and makes cross-region queries the expensive exception. Uber-style designs geo-shard by city for exactly this reason.

Trade-offPlacement control in exchange for an extra hop and a new single point of failure — the directory has to be more available than the shards it fronts.

The shard key is the whole decision

A good shard key has high cardinality, spreads load evenly, and aligns with the dominant access pattern — the queries you shard for become single-shard and fast, everything else becomes scatter-gather and slow. Say that sentence explicitly: "user-scoped queries fast, global queries slow" is the trade being made.

The canonical trap is the celebrity: shard tweets by user_id and one account with 100M followers cooks a single shard — the Justin Bieber problem. And know the full bill: no cross-shard joins (denormalise or join in the app), no cross-shard transactions (sagas or 2PC pain), and unique IDs need generating without a single counter.

Trade-offWhatever key you pick, the queries that don't include it pay a fan-out to every shard — choose for the 95% query and name the 5% you sacrificed.
04

Numbers worth memorising

Shard trigger, writes~5k–10k writes/s on one node
Shard trigger, storage~2–4 TB comfortable per Postgres/MySQL node
Logical partition headroomprovision 2–4× current need in logical shards
Redis Cluster slots16384 fixed hash slots mapped to nodes
Scatter-gather costlatency = slowest shard × every global query
05

Where the interviewer pushes

The standard push is the hot shard: "you sharded Twitter by user_id — what happens to Justin Bieber?" The strong answer concedes the design honestly: his shard melts on reads, so celebrity content gets served from cache and fan-out paths rather than shard reads, or I switch the tweet store to shard by tweet_id and pay scatter-gather on profile timelines instead. The point being scored is that I can name which access pattern each key choice sacrifices — there is no key that wins both.

The second push is resharding: "you need to add shards without downtime — go." Weak answers say "consistent hashing" and stop. The full mechanics: with fixed logical partitions I move whole slots — double-write to old and new homes, backfill history, verify counts, cut reads over, drop the old copy. It's operationally scary and slow by design, which is exactly why I provisioned 2–4× logical shards up front — the best resharding is the one you deferred for three years.

Shows up in

Found a bug or want more?

Tell me what's broken, missing, or wrong. Every message lands in my inbox.