Overview
Sharding splits a dataset horizontally across multiple database servers (shards), each holding a subset of rows, so that data volume and write traffic can grow beyond one machine. A shard key (such as user_id or tenant_id) decides which shard owns each row, and a routing layer sends each query to the right shard.
Sharding is powerful but costly: cross-shard queries, joins, and transactions become hard; resharding when shards fill up requires moving data; and a poor shard key creates hot shards. It is usually the last step after indexing, caching, replicas, and vertical scaling are exhausted.
Books by authors A-F live in branch 1, G-M in branch 2, and so on. Finding a book by author is quick if you know which branch to visit. Finding every book published in 1999 means visiting every branch.
When to use it
- Write throughput or data size exceeds a single primary.
- Data naturally partitions by a key (tenant, user, region).
- Most queries include that key.
- Isolation between tenants or regions is valuable.
Where it shows up in interviews
Recognize it when: billions of rows, heavy write load.
- Design Twitter's tweet storage
- Design Instagram photos metadata
- Design a URL shortener at scale
Recognize it when: many customers of very different sizes.
- Design Slack's data layer
- Design a B2B analytics SaaS
Where it is used in real software
Sharded PostgreSQL by user ID into thousands of logical shards mapped to fewer physical servers, making rebalancing easier.
Created at YouTube to shard MySQL transparently; used by Slack, GitHub, and others.
Notion sharded PostgreSQL by workspace ID into 480 logical shards as it outgrew a single database.
Key terms
- Shard key
- Column(s) that decide which shard owns a row.
- Range sharding
- Contiguous key ranges per shard; good for range scans, prone to hot spots.
- Hash sharding
- hash(key) mod N or consistent hashing; even spread, no range scans.
- Directory sharding
- A lookup table maps keys to shards; flexible but the directory is critical.
- Logical shards
- Many small virtual shards mapped to fewer physical nodes, easing rebalancing.
How it works, step by step
- 1Choose the shard key
High cardinality, even load, present in most queries, and keeps related data together.
- 2Choose the strategy
Hash for even spread, range for scans, directory for flexibility.
- 3Create many logical shards
For example 1,024 logical shards on 8 servers, so rebalancing moves whole logical shards.
- 4Route queries
Application library, proxy (Vitess, ProxySQL), or the database itself (Citus).
- 5Handle cross-shard needs
Scatter-gather for rare queries, denormalized global indexes, and sagas instead of distributed transactions.
STEP 1The router computes hash(4217) mod 1024 = logical shard 369.
Choosing a shard key for a social app
Candidate keys and their effects
| Shard key | Distribution | Query locality | Verdict |
|---|---|---|---|
| user_id (hashed) | Even | User's own data on one shard | Good for profile and posts |
| created_at (range) | All new writes on the latest shard | Time-range scans local | Hot shard: bad for writes |
| country | Very uneven (large countries) | Regional queries local | Hot spots |
| post_id (hashed) | Even | A user's posts spread everywhere | Timeline queries fan out |
NOWShard key: user_id (hashed) | Distribution: Even | Query locality: User's own data on one shard | Verdict: Good for profile and posts
The best key matches the dominant access pattern. Celebrities with huge data may still need special handling (splitting or dedicated shards).
Implementation
import { createHash } from "node:crypto"; const LOGICAL_SHARDS = 1024;// Mapping of logical shard ranges to physical databases (kept in config or a directory service)const physical = [ { from: 0, to: 255, pool: shardPool0 }, { from: 256, to: 511, pool: shardPool1 }, { from: 512, to: 767, pool: shardPool2 }, { from: 768, to: 1023, pool: shardPool3 },]; function logicalShard(userId: string) { return createHash("md5").update(userId).digest().readUInt32BE(0) % LOGICAL_SHARDS;} function poolFor(userId: string) { const shard = logicalShard(userId); return physical.find((p) => shard >= p.from && shard <= p.to)!.pool;} export const getPosts = (userId: string) => poolFor(userId).query("SELECT * FROM posts WHERE user_id = $1 ORDER BY created_at DESC LIMIT 20", [userId]); // Rare cross-shard query: scatter-gather with a limit per shardexport async function recentGlobal(limit = 20) { const results = await Promise.all(physical.map((p) => p.pool.query("SELECT * FROM posts ORDER BY created_at DESC LIMIT $1", [limit]))); return results.flatMap((r) => r.rows).sort((a, b) => b.created_at - a.created_at).slice(0, limit);}Complexity and performance
Routed to one node.
Latency = slowest shard.
If the key distributes well.
Trade-offs
Sharding scales writes and storage nearly linearly but makes joins, transactions, schema changes, and operations harder.
Hash avoids hot spots but loses range scans; range supports scans but concentrates recent writes.
Variants and related techniques
Each tenant on one shard; big tenants get dedicated shards.
Shard by region for data residency and latency.
MongoDB, Cassandra, DynamoDB, Spanner, and CockroachDB shard and rebalance automatically.
Common mistakes
- Sharding too early.
Fix: Exhaust indexes, caching, replicas, and vertical scaling first.
- hash(key) mod N with physical shards.
Fix: Changing N remaps everything; use logical shards or consistent hashing.
- Global auto-increment IDs.
Fix: Use Snowflake-style IDs or UUIDs that embed or allow shard routing.
Interview questions
How do you choose a shard key?
Pick a key with high cardinality that spreads load evenly, appears in most queries so they hit a single shard, and keeps data that is queried together on the same shard, such as user_id or tenant_id.
How do you reshard without downtime?
Use many logical shards so moving data means moving whole logical shards. Copy a logical shard to the new server, replicate ongoing changes (CDC), briefly pause writes to switch the mapping, then delete the old copy.
How do you handle queries that do not include the shard key?
Maintain a secondary index or lookup table (for example email to user_id), denormalize into a search system, or scatter-gather for rare admin queries.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Pick shard keys for 4 tables in an e-commerce app | Medium | Locality. |
| Design resharding from 4 to 16 servers | Hard | Logical shards and migration. |
| Design sharding for a multi-tenant SaaS with whales | Hard | Uneven tenants. |