Backend · Databases
Sharding vs. Partitioning: When and Why to Shard
Partitioning and sharding get used interchangeably, but only one of them buys you more write capacity — here's the distinction, and the checklist for when sharding is actually the right call.
"Partitioning" and "sharding" get thrown around as synonyms so often that most engineers use them interchangeably — right up until a design review where someone asks "wait, are these shards or partitions?" and nobody has the same answer. The confusion isn't just semantic. The two techniques solve different problems, carry different operational costs, and knowing which one you actually need is the difference between a clean scaling story and a self-inflicted distributed-systems nightmare.
Partitioning vs. sharding: the distinction that gets blurred
Partitioning is the general act of splitting a large dataset into smaller, more manageable pieces. Critically, partitioning says nothing about where those pieces live — a partitioned table can sit entirely on a single database instance. Split a 500-million-row orders table into monthly partitions and you still have one server, one instance of the database engine, and one point of connection. You've made queries and maintenance faster by letting the engine skip partitions that can't match a filter, but you haven't added a single byte of additional capacity to the system as a whole.
Sharding is a specific, stricter form of partitioning: the pieces are distributed across independent nodes — separate machines, each running its own copy of the database engine, each with its own CPU, memory, and disk. A shard isn't just a slice of data; it's an autonomous unit that can be queried, backed up, and scaled without touching any other shard. That independence is the entire point, and it's also where all the operational complexity in this article comes from.
Put simply: all sharding is partitioning, but not all partitioning is sharding. Table partitioning within one PostgreSQL instance is partitioning without sharding. Splitting a users table across sixteen independent MySQL instances by user_id is sharding — and it happens to also be partitioning, since each shard typically holds a partition of the logical dataset.
Sharding key strategies
However you shard, you need a function that maps a row to a shard. The choice of sharding key and strategy shapes almost everything downstream — query patterns, hotspot risk, and how painful resharding will eventually be.
- Range-based sharding — assign contiguous ranges of the key to each shard (shard 1 holds IDs 0–999,999, shard 2 holds 1,000,000–1,999,999, and so on). This makes range scans and ordered queries cheap, since a query for "all orders from March" touches one or two shards instead of all of them. The risk is hotspots: if the key correlates with time or insertion order — auto-incrementing IDs, timestamps — all new writes pile onto whichever shard currently owns the newest range, while older shards sit idle.
- Hash-based sharding — run the key through a hash function and mod it by the shard count. Writes spread evenly across shards almost by construction, which makes this the default choice for write-heavy workloads. The cost is that range queries fall apart: "all orders from March" now scatters across every shard, because hashing deliberately destroys any notion of adjacency between keys.
- Directory-based (lookup) sharding — keep an explicit mapping table (key → shard) that a lookup service consults on every request. This is the most flexible option — you can rebalance by moving individual keys rather than recomputing a hash or range boundary — but it adds a hop and a dependency: every query now goes through the directory first, and that directory itself has to be fast and highly available, or it becomes the new bottleneck.
Many production systems land on a hybrid: hash the key to spread load evenly, but hash within logical groups (a tenant ID, a customer ID) so that a single customer's data still lives on one shard and can be queried without fanning out everywhere.
Why sharding is fundamentally a write-scalability lever
It's worth being precise about what sharding actually buys you, because it's easy to reach for it when the real problem is something else entirely. A single database node — no matter how generous the hardware, how well-tuned the queries, or how large the cache — has a hard ceiling on two things: how much data it can store, and how many writes per second it can absorb. You can push that ceiling higher with faster disks, more memory, and better indexes, but it's still a ceiling, and eventually every growing system hits it.
Read replicas are the tool most teams reach for first, and they work well for read scaling — fan reads out across as many replicas as you like, and read throughput scales roughly linearly. But replicas don't touch the write problem at all. Every write still has to land on the single primary first, then propagate outward. Add fifty read replicas to a struggling primary and its write ceiling hasn't moved an inch — you've scaled reads while the actual bottleneck sits exactly where it was.
Sharding is the mechanism that removes that ceiling, because it gives you more independent writers, not just more independent readers. Ten shards means ten primaries, each accepting writes for its own slice of the data, in parallel. That's the one thing no amount of caching, indexing, or replica fan-out can deliver on a single node: sharding is fundamentally about horizontally scaling write capacity and storage, by trading one big node for many smaller, independent ones.
When to shard
Because sharding is expensive to adopt and even more expensive to undo, it should be the last lever you pull, not the first. Reach for it when several of these signals are true at once — not just one:
- Sustained write throughput is approaching the ceiling of what a single primary can absorb, even after query tuning, batching writes, and upgrading hardware.
- Dataset size exceeds what fits comfortably on one node — working sets no longer fit in memory, index maintenance and backups take increasingly long, and vacuum or compaction jobs start competing with live traffic.
- Hot partitions persist after other optimizations — you've already tried better indexing, query rewrites, and caching, and a subset of the data is still overwhelming the node it lives on.
- You've exhausted the simpler options first — vertical scaling (bigger instance), read replicas (for read-heavy load), caching layers, and query/index optimization all fall short of what's needed. Sharding solves a real ceiling; it shouldn't be the answer to a slow query that indexing would have fixed in an afternoon.
Sharding is a last resort, not a first instinct
A huge fraction of "we need to shard" conversations are actually "we need a better index" or "we need a cache" conversations. Sharding solves one specific problem — a single node's write or storage ceiling — and introduces real, permanent complexity to solve it. Exhaust the cheaper options first; they're reversible, and sharding isn't.
The real costs of sharding
Sharding doesn't just add capacity — it changes what your database is capable of doing at all, and every one of these costs shows up eventually:
- Cross-shard joins and transactions become hard or impossible. A join that used to be a single SQL statement now means fetching data from multiple shards over the network and joining it in application code. A transaction that needs atomicity across shards needs a distributed transaction protocol (two-phase commit, sagas) — dramatically more complex and slower than a local
BEGIN/COMMIT. - Resharding and rebalancing are operationally painful. Adding shards to relieve a hot node means moving live data between machines without downtime or data loss — one of the hardest operations in distributed systems, and one you'll need to repeat every time growth outpaces your shard count.
- Uneven shard keys create hot shards. Pick a key that correlates with activity — a celebrity user, a large tenant, a popular product — and that one shard absorbs disproportionate load while its siblings idle, recreating the exact hotspot problem sharding was supposed to solve, just at a coarser granularity.
Choosing deliberately
Partitioning within a single node is nearly always worth doing early — it's cheap, reversible, and speeds up maintenance and queries with little downside. Sharding is a different category of decision entirely: it's an architectural commitment that touches your schema, your query patterns, your transaction guarantees, and your operational tooling, all at once.
Sharding is a one-way door
Delaying a sharding decision costs you some performance headroom. Reversing one — merging shards back into fewer nodes, or unwinding application code that assumes data is distributed — costs you months of migration work and carries real risk of data loss along the way. When in doubt, delay: it's far cheaper to shard a year late than to have sharded a year too early.
Key takeaways
- Partitioning splits data into smaller pieces that can still live on one node; sharding specifically distributes those pieces across independent nodes.
- Range-based keys enable efficient range scans but risk hotspots; hash-based keys spread load evenly but break range queries; directory-based keys are flexible but add an indirection hop.
- Sharding scales write throughput and storage — read replicas only scale reads, since all writes still funnel through one primary.
- Shard only after exhausting indexing, caching, vertical scaling, and read replicas, and only when write throughput, dataset size, or hot partitions are hitting a real single-node ceiling.
- The real cost is cross-shard joins/transactions, painful resharding, and hot shards from uneven keys.
- Sharding is a one-way architectural door — it's far easier to delay it than to reverse it.
