Every database eventually runs out of room to grow on a single machine. You can buy a bigger box for a while — more RAM, faster disks, more cores — but vertical scaling has a ceiling, and it's an expensive one. At some point, the only way forward is to split your data across multiple machines. That's sharding.
This post walks through what sharding actually is, the main strategies for doing it, the problems it introduces, and how to decide whether you need it at all.
What Sharding Actually Is
Sharding is horizontal partitioning: you split a single logical dataset into smaller pieces called shards, and you distribute those shards across multiple database instances. Each shard holds a subset of the rows, but all shards share the same schema.
This is different from replication, which copies the entire dataset onto multiple machines for redundancy and read scaling. Sharding and replication solve different problems and are often used together — each shard might itself be replicated for durability.
It's also different from vertical partitioning, which splits a table by columns rather than rows (e.g., putting rarely-used columns in a separate table). Sharding splits by rows: each shard is a full-schema slice of a fraction of your data.
Why Shard
The case for sharding usually comes down to one or more of these:
- Write throughput. A single primary database node can only accept so many writes per second. Sharding spreads writes across multiple nodes, each handling only its slice of traffic.
- Storage limits. Some datasets are simply too large to fit on one machine's disk, or too large to keep an effective portion of the working set in memory or cache.
- Query latency under load. Smaller per-node datasets mean smaller indexes, faster scans, and less contention.
- Blast radius. A failure or maintenance window on one shard doesn't take down 100% of your data — only the fraction that lives there.
Sharding is not usually the first lever to pull. Read replicas, caching layers, query optimization, connection pooling, and vertical scaling are cheaper and simpler, and they solve a large fraction of scaling problems without the complexity sharding introduces. Reach for sharding when you've exhausted those options and write volume or data size is still the bottleneck.
Choosing a Shard Key
The shard key (or partition key) is the field used to decide which shard a given row lives on. This is the single most consequential decision in a sharding design — it's difficult to change later without a full data migration, and a poor choice creates problems that compound as the dataset grows.
A good shard key should:
- Distribute load evenly. Avoid keys that concentrate a disproportionate share of traffic or data on one value (a single huge tenant, a single popular product).
- Match your access patterns. If most queries filter or join on a particular field, sharding on that field lets those queries stay within a single shard.
- Have high cardinality. Enough distinct values to spread across all your shards, now and as you add more.
Common candidates: user ID, tenant ID (in multi-tenant SaaS), geographic region, or a hash of one of these.
Sharding Strategies
Range-Based Sharding
Rows are assigned to shards based on ranges of the shard key — for example, user IDs 1–1,000,000 on shard A, 1,000,001–2,000,000 on shard B, and so on.
Pros: Range queries (e.g., "all users created in March") stay on one or a few shards. Straightforward to reason about.
Cons: Prone to hotspots. If IDs are assigned sequentially and new users are the most active ones, all write traffic piles onto the most recently created shard.
Hash-Based Sharding
The shard key is passed through a hash function, and the output determines the shard. This spreads rows pseudo-randomly across all shards regardless of the key's natural distribution.
Pros: Even distribution of both storage and load, even when the underlying key values are skewed or sequential.
Cons: Range queries become expensive, since consecutive keys land on unrelated shards — you generally have to scatter-gather across every shard and merge results.
Consistent Hashing
A refinement of hash-based sharding designed to minimize data movement when shards are added or removed. Instead of hash(key) % N, keys and shards are placed on a conceptual ring, and each key belongs to the next shard clockwise from it. Adding or removing a shard only affects the keys adjacent to it on the ring, not the entire dataset.
This is the strategy behind systems like DynamoDB's and Cassandra's partitioning, and it's the standard answer to "how do we shard without a full re-shuffle every time we scale?"
Directory-Based (Lookup Table) Sharding
A separate service or table maps each shard key (or range of keys) to a specific shard. This adds an extra hop and a new point of failure, but it decouples the mapping from any formula, which means you can rebalance by updating the directory rather than recomputing a hash or moving range boundaries.
Geo-Based Sharding
Data is partitioned by geography — European user data lives in EU shards, North American data in US shards, and so on. This is often driven by data residency and latency requirements as much as by scaling needs, and is common in systems that need to comply with regulations like GDPR.
The Hard Problems Sharding Introduces
Sharding isn't free. Here's what you take on in exchange for horizontal scale:
Cross-shard queries and joins. A query that needs data from multiple shards (a join across two entities that live on different shards, or an aggregate across the whole dataset) can no longer be satisfied by one node. You either scatter-gather and merge in the application layer, denormalize the data so the join isn't needed, or maintain a separate reporting/analytics store that isn't sharded the same way.
Rebalancing. As data grows unevenly, some shards fill up or get hotter than others. Moving data between shards to rebalance is operationally expensive and risky, particularly with range-based or naive hash-based schemes. Consistent hashing exists specifically to reduce this cost.
Distributed transactions. An operation that needs to atomically update rows on two different shards can't rely on a single-node transaction. You need two-phase commit, sagas, or you need to design your data model so cross-shard transactions simply don't happen.
Uneven load ("hotspots"). Even with a well-chosen shard key, real-world traffic is rarely perfectly uniform. A celebrity account, a viral product, or a large enterprise tenant can overload a single shard regardless of how the key was chosen.
Operational complexity. Schema migrations, backups, monitoring, and failover all need to happen consistently across every shard. What was one database to operate becomes N databases, each needing the same care.
Application-level awareness. Unless you're using a database or middleware layer that shards transparently, your application needs to know which shard to query — which adds a routing layer and makes ad hoc queries harder to run.
Do You Actually Need to Shard?
Before committing to sharding, it's worth checking whether cheaper options are exhausted:
- Have you added read replicas for read-heavy load?
- Have you added caching (Redis, Memcached) in front of hot queries?
- Have you optimized your indexes and slow queries?
- Have you tried vertical scaling — a bigger instance?
- Have you considered a managed, horizontally-scalable database (CockroachDB, Vitess-backed MySQL, DynamoDB, Cassandra) that handles sharding internally, rather than building it yourself?
Sharding is usually the right call when write volume or total data size genuinely exceeds what a single well-tuned node — plus replicas and caching — can handle, and when a managed distributed database doesn't fit your constraints (cost, existing schema, specific consistency requirements).
Closing Thoughts
Sharding solves a real problem: it lets you scale writes and storage past what any single machine can hold. But it trades that scale for real complexity — in query design, in transactions, in operations, and in the application layer. Choose your shard key carefully, prefer consistent hashing if you expect to add shards over time, and exhaust the simpler scaling options first. The best sharding architecture is often the one you delay adopting for as long as you reasonably can.