All chapters in this track
- 01Chapter 1: Why Database Choices Matter
- 02Chapter 2: Relational Fundamentals: Tables, Keys, Constraints, Normalization
- 03Chapter 3: ACID Properties and Transactions
- 04Chapter 4: Isolation Levels and the Anomalies Each One Prevents
- 05Chapter 5: SQL Essentials and Query Optimization
- 06Chapter 6: Indexing Strategies: B-Tree, Hash, Composite, Covering
- 07Chapter 7: NoSQL Categories: Document, Key-Value, Wide-Column, Graph
- 08Chapter 8: Schema Design and Data Modeling (Relational and NoSQL Access Patterns)
- 09Chapter 9: The CAP Theorem and Consistency Models
- 10Chapter 10: Replication: Leader-Follower and Multi-Leader
- 11Chapter 11: Sharding and Partitioning Strategies
- 12Chapter 12: Caching Layers and Invalidation Strategies
- 13Chapter 13: Evolving Schemas Safely in Production
- 14Chapter 14: Diagnosing and Fixing a Slow Query, End to End
- 15Chapter 15: Schema Design Interview Exercises
- 16Chapter 16: Database Concepts in System Design and Production Debugging
Chapter 11 of 16
Chapter 11: Sharding and Partitioning Strategies
Why this matters
Replication (Chapter 10) solves availability and read scaling by copying the same data to multiple nodes — but it doesn't solve the problem of a dataset (or write load) becoming too large for any single node to handle at all. Sharding (splitting data across multiple nodes, where each node holds a different subset of the data) is the tool for that specific problem, and choosing a bad sharding key is one of the most consequential, hardest-to-reverse mistakes in database scaling.
The core concept
[BEGINNER] Sharding (also called horizontal partitioning) splits a dataset across multiple database nodes, where each shard holds a distinct subset of the rows — as opposed to replication, where every node holds a complete copy of the same data. Sharding lets total data volume and write throughput scale beyond what any single machine could handle, at the cost of significant added complexity: queries spanning multiple shards, transactions across shards, and rebalancing when shards grow unevenly all become genuinely harder problems than in a single-node system.
Unsharded: ONE database node holds ALL customer data (limited by that one machine's capacity)
Sharded:
Shard 1 (customers A-H) Shard 2 (customers I-P) Shard 3 (customers Q-Z)
Each shard is a separate database instance, holding only ITS slice of the data.
Sharding strategies
[BEGINNER]
Range-based sharding — split by ranges of the shard key's value (e.g., customer ID 1-1,000,000 on shard 1, 1,000,001-2,000,000 on shard 2):
Trade-off: range queries within one shard are efficient (data is contiguous),
but data distribution can become badly UNEVEN over time -- if customer IDs are
assigned sequentially and older customers are more active, early shards can
become disproportionately "hot" while later ones stay comparatively idle.
Hash-based sharding — apply a hash function to the shard key, and use the hash to determine which shard a row belongs to:
shard_number = hash(customer_id) % number_of_shards
Trade-off: distributes data much more evenly across shards (a good hash function
scatters values uniformly, avoiding the range-based "hot early shard" problem),
but destroys any meaningful physical ordering -- a range query ("all customers
with IDs between X and Y") now has to check EVERY shard, since a hash-based
scheme provides no guarantee that a contiguous ID range lives on one shard.
Directory-based sharding — maintain an explicit, separate lookup table mapping each shard key to its shard, rather than computing the mapping algorithmically:
Trade-off: maximum flexibility (shards can be rebalanced by simply updating the
lookup table, without needing to physically move data according to a rigid
formula) at the cost of an additional lookup step on every single query, and
the lookup table itself becoming a new critical dependency (and potential
bottleneck/single point of failure) that must itself be made highly available.
Choosing a shard key: the single most consequential decision
[SENIOR INSIGHT] A poorly chosen shard key is one of the hardest mistakes to fix after the fact, because fixing it means re-sharding — physically moving large volumes of data between shards while the system is live, a genuinely difficult, risky, often multi-week production operation. The two properties a good shard key needs, together:
- Even distribution — writes and storage should spread roughly evenly across shards; a poorly chosen key can create a hot shard that receives dramatically more traffic than the others, defeating the entire purpose of sharding (this is precisely the range-based sharding risk illustrated above).
- Query alignment — the shard key should match the field most commonly used to filter queries; a query that doesn't include the shard key in its filter condition must be broadcast to every shard and have results merged (a scatter-gather query), which is far slower and more resource-intensive than a query that can be routed directly to the one relevant shard.
Good shard key example: sharding an orders table by customer_id, when the
dominant query pattern is "get all orders for customer X" -- this query
routes directly to exactly one shard, and (assuming customers generate
roughly similar order volumes) distributes load evenly.
Bad shard key example: sharding the same orders table by order STATUS
(pending/shipped/delivered) -- this creates severely UNEVEN shards (most
orders are eventually "delivered," so that shard grows enormous while
"pending" stays small), AND most useful queries ("all orders for a customer")
would need to scatter-gather across every status shard, since a customer's
orders are scattered across all status values.
Common mistakes
- Choosing a shard key based on what seems like a natural identifier, without analyzing actual query patterns and write distribution — the customer_id-vs-status example above shows how two "reasonable-sounding" choices can have dramatically different real-world outcomes.
- Not anticipating that a shard key providing even distribution TODAY might not stay even as the system evolves — a shard key that was fine at launch can become uneven as usage patterns shift, requiring a difficult re-sharding operation to correct later.
- Building a system that requires frequent scatter-gather queries (queries not aligned with the shard key), then being surprised these queries perform far worse than expected — always check whether a query's primary filter aligns with the chosen shard key.
- Sharding before it's actually needed. Sharding adds substantial operational complexity (cross-shard transactions become hard or impossible, rebalancing is a real engineering project, application code must be shard-aware) — this complexity should be adopted in response to a genuine, measured scaling need (a single node genuinely can't handle the data volume or write throughput), not preemptively "to be ready for scale" that may never materialize (the same premature-optimization theme running throughout this book, applied here at its highest-stakes point).
Checklist: sharding decisions
- Only shard once a single node's capacity (storage or write throughput) is a real, measured constraint, not preemptively.
- Choose a shard key based on actual, dominant query patterns AND even distribution of writes/storage — analyze both before committing, since the decision is extremely expensive to reverse.
- Minimize scatter-gather queries by choosing a shard key aligned with the most common, most performance-critical query filter.
- Plan for shard rebalancing from the start, even if range/hash-based sharding is chosen initially — data distribution patterns can shift over a system's lifetime in ways the original sharding scheme didn't anticipate.
Discussion / interview questions
-
"What's the difference between sharding and replication?" Strong answer: Replication copies the SAME complete dataset to multiple nodes (for availability and read scaling); sharding splits the dataset into DIFFERENT subsets across multiple nodes (for total data volume and write throughput scaling) — they solve different problems and are often used together.
-
"What makes a good sharding key, and what happens if you choose a bad one?" Strong answer: A good key provides even distribution across shards and aligns with the dominant query pattern; a bad key creates hot shards (uneven load) and/or forces frequent scatter-gather queries across every shard — and fixing a bad shard key after the fact requires a difficult, risky re-sharding operation.
-
"Compare range-based and hash-based sharding." Strong answer: Range-based keeps data physically ordered (efficient range queries) but risks uneven distribution over time (hot shards); hash-based distributes data evenly but destroys physical ordering, making range queries require checking every shard.
-
"Why is sharding often recommended as a last resort, adopted only when genuinely needed?" Strong answer: Sharding adds substantial operational and application complexity — cross-shard transactions/queries become difficult, rebalancing is a real engineering effort, and shard key mistakes are expensive to fix — this complexity should be justified by a real, measured scaling constraint, not adopted preemptively.
Chapter summary / cheat sheet
- Sharding splits data across nodes (each holding a different subset); replication (Chapter 10) copies the same data across nodes — different problems, often combined.
- Range-based sharding: ordered, efficient range queries, but risks uneven ("hot") shards over time. Hash-based sharding: even distribution, but destroys physical ordering for range queries. Directory-based: flexible rebalancing, at the cost of an extra lookup step and a new critical dependency.
- A good shard key needs both even distribution and alignment with the dominant query pattern — getting this wrong is one of the most expensive mistakes to fix later (requiring live re-sharding).
- Shard only in response to a real, measured single-node capacity constraint — not preemptively, given the substantial complexity it introduces.