Sharding Concepts
Sharding is MongoDB’s method for horizontal scaling: instead of putting all your data on one server, you split (“partition”) a collection’s documents across multiple machines called shards, so that no single server has to hold the entire data set or handle the entire query load. It is the tool you reach for when a single replica set can no longer hold your working set in memory or can no longer keep up with write throughput — not a default step every growing application needs. Understanding how MongoDB decides which document goes on which shard is the single most important thing to get right, because that decision is very hard to change after the fact.
Overview: How Sharding Works
A sharded MongoDB cluster is made of three kinds of components working together. Shards are where the actual data lives — each shard is itself a replica set (a primary plus secondaries) for redundancy, and each shard holds a subset of the collection’s documents. Config servers (also a replica set) store the cluster’s metadata: which ranges of shard key values live on which shard, the cluster’s settings, and authentication data. mongos is a lightweight, stateless routing process that your application actually connects to; it asks the config servers where data lives and forwards each operation to the correct shard (or shards).
Sharding happens per-collection, not per-database. When you shard a collection, you choose a shard key — one field, or a compound set of fields, present in every document. MongoDB uses the values of the shard key to split the collection into contiguous ranges called chunks (by default targeted around 128MB, though this is now controlled by chunk auto-splitting policy rather than a hard cap in recent versions). Each chunk is assigned to exactly one shard. As chunks grow or become imbalanced across shards, a background process called the balancer migrates chunks between shards so that data (and therefore load) stays roughly even.
This is fundamentally different from a single replica set, where every mongod has a full copy of every document. In a sharded cluster, a given document physically exists on exactly one shard (ignoring its replica set copies), and the shard key is what determines which one. Choose it well and reads/writes land on the right shard directly; choose it poorly and you get “hot” shards that absorb all the traffic while the others sit idle.
Key Components at a Glance
| Component | Role |
|---|---|
mongos |
Query router; the process your application connects to. Routes each operation to the right shard(s). |
| Config servers | Replica set storing cluster metadata: chunk ranges, shard assignments, cluster settings. |
| Shard | A replica set holding a portion of the sharded collection’s documents. |
| Shard key | The field(s) MongoDB uses to decide which shard a document belongs on. |
| Chunk | A contiguous range of shard key values, MongoDB’s unit of data movement between shards. |
| Balancer | Background process that migrates chunks to keep data distribution even across shards. |
Syntax
Enabling sharding is a two-step process at the database level and then the collection level, run from mongosh connected to a mongos router:
// Enable sharding for a database
sh.enableSharding("myDatabase");
// Create a supporting index that matches the shard key pattern
db.myCollection.createIndex({ shardKeyField: 1 });
// Shard the collection using the shard key pattern
sh.shardCollection("myDatabase.myCollection", { shardKeyField: 1 });
sh.enableSharding(database)—databaseis the string name of the database. This must run once before any collection in that database can be sharded.db.collection.createIndex(shardKeyPattern)— the collection must already have an index whose prefix matches the shard key pattern before you can shard it (unless the collection is empty, in which caseshardCollectioncreates it for you).sh.shardCollection(namespace, key, unique, options)—namespaceis the"database.collection"string;keyis the shard key pattern document, e.g.{ customerId: 1 }for ranged sharding or{ deviceId: "hashed" }for hashed sharding;unique(optional boolean) enforces uniqueness on the shard key;options(optional object) can set things likenumInitialChunksfor pre-splitting a hashed collection.
Examples
Example 1: Sharding an orders collection with a compound range key
Suppose ecommerce.orders is large and mostly queried by customer. A compound shard key of customerId then orderDate keeps each customer’s orders together and clustered by time, which is great for range queries scoped to one customer.
sh.enableSharding("ecommerce");
db.orders.createIndex({ customerId: 1, orderDate: 1 });
sh.shardCollection("ecommerce.orders", { customerId: 1, orderDate: 1 });
Output:
{
collectionsharded: 'ecommerce.orders',
collectionUUID: UUID('e1a2b3c4-1234-5678-9abc-def012345678'),
ok: 1
}
MongoDB confirms the collection is now sharded. Behind the scenes it registers the shard key in the config metadata and creates the initial chunk(s) covering the full range of possible customerId/orderDate values, all initially on one shard until the balancer distributes them.
Example 2: Sharding a high-volume events collection with a hashed key
A device telemetry collection is written to constantly with a naturally increasing-ish deviceId pattern and needs writes spread evenly across shards rather than clustered by range. A hashed shard key solves this by hashing the field value before deciding placement.
sh.shardCollection("ecommerce.events", { deviceId: "hashed" });
Output:
{
collectionsharded: 'ecommerce.events',
collectionUUID: UUID('9f8e7d6c-5432-1abc-9def-0123456789ab'),
ok: 1
}
Hashing scrambles the shard key values so that even sequential or clustered input values land on essentially random shards, which is ideal for maximizing write throughput but sacrifices the ability to do efficient range queries on that field (a range query on a hashed key can’t target a contiguous set of chunks, so it becomes a scatter-gather across all shards).
Example 3: Checking how data is distributed across shards
After a cluster has been running for a while, you can check whether documents are actually spread evenly:
db.orders.getShardDistribution();
Output:
Shard shard01 at shard01/host1:27017,host2:27017
data: 340MiB docs: 512000 chunks: 6
estimated data per chunk: 56.6MiB
estimated docs per chunk: 85333
Shard shard02 at shard02/host3:27017,host4:27017
data: 355MiB docs: 528000 chunks: 6
estimated data per chunk: 59.1MiB
estimated docs per chunk: 88000
Totals
data: 695MiB docs: 1040000 chunks: 12
Shard shard01 contains 48.92% data, 49.23% docs in cluster, avg obj size on shard: 696B
Shard shard02 contains 51.08% data, 50.77% docs in cluster, avg obj size on shard: 706B
Roughly equal percentages across shards mean the chosen shard key is distributing data well. A cluster where one shard holds 90%+ of the data is a strong signal that the shard key needs to be reconsidered.
How It Works Step by Step
When your application sends a query through mongos, the router first checks whether the query includes the shard key (or a prefix of a compound shard key). If it does, mongos consults the config servers’ cached chunk map and forwards the query directly to the one (or few) shard(s) that could possibly hold matching documents — a targeted operation. If the query does not include the shard key at all, mongos has no way to know where matching documents might be, so it must broadcast the query to every shard and merge the results — a scatter-gather operation, which is far more expensive and doesn’t scale the way targeted queries do.
On writes, MongoDB tracks the size and document count of each chunk. When a chunk grows past the configured threshold, MongoDB automatically splits it into two smaller chunks along the shard key range — a metadata-only operation, no data actually moves yet. Separately, the balancer process periodically compares the number of chunks per shard; if the difference exceeds a threshold, it migrates chunks from over-loaded shards to under-loaded ones. A migration copies all documents in that chunk’s range to the destination shard, then atomically updates the config metadata and deletes the original copy — existing reads and writes are not blocked for the whole migration, but there is a brief critical section at the end.
Common Mistakes
Mistake 1: Using a monotonically increasing shard key
Fields like _id (an ObjectId, which embeds a timestamp) or an incrementing counter always sort in roughly the same direction over time. Every new document’s key is greater than nearly all existing keys, so every new document — and therefore every write — lands on the same chunk, on the same shard, no matter how many shards you have.
// Wrong: _id is monotonically increasing, so all new writes hit one shard
sh.shardCollection("ecommerce.events", { _id: 1 });
Use a hashed key (or a compound key led by a well-distributed field) so new writes spread across shards instead of funneling into one:
sh.shardCollection("ecommerce.events", { deviceId: "hashed" });
Mistake 2: Choosing a low-cardinality shard key
A field like status with only a handful of possible values ("pending", "shipped", "delivered") can only ever be split into as many meaningfully distinct chunks as there are distinct values, no matter how large the collection grows. Most documents pile onto whichever value is most common, creating a hot, oversized, unsplittable chunk.
// Wrong: only a few distinct status values exist, chunks can't split usefully
sh.shardCollection("ecommerce.orders", { status: 1 });
Prefer a field (or compound key) with high cardinality and an even value distribution, such as customerId combined with a second field:
sh.shardCollection("ecommerce.orders", { customerId: 1, orderDate: 1 });
Mistake 3: Querying without the shard key
Once a collection is sharded, queries that omit the shard key can’t be routed to a single shard and fall back to scatter-gather, which negates most of the performance benefit of sharding in the first place.
// Inefficient: no customerId, mongos must broadcast to every shard
db.orders.find({ orderDate: { $gte: ISODate("2026-01-01") } });
// Targeted: includes the shard key prefix, routes to one shard
db.orders.find({ customerId: "CUST-88421", orderDate: { $gte: ISODate("2026-01-01") } });
Best Practices
- Pick a shard key based on your real query patterns first, cardinality and distribution second — a key you can’t query by is nearly as bad as one that’s unevenly distributed.
- Favor compound shard keys over a single field; they give you more flexibility to keep related data together while still spreading writes.
- Use a hashed shard key when write throughput matters more than range-query efficiency on that field, and avoid it when you need efficient range scans.
- Shard collections while they’re still small if you know they’ll grow large — sharding an already-massive collection means a long, resource-heavy initial chunk migration.
- Never shard a collection on a field you might need to change later; changing a shard key traditionally requires exporting and reimporting all the data (recent MongoDB versions add limited
reshardCollectionsupport, but it’s still a heavyweight operation). - Monitor
getShardDistribution()and the balancer status regularly, not just at setup time — access patterns drift as an application evolves. - Don’t reach for sharding as a default scaling strategy; a properly indexed, appropriately sized single replica set handles the vast majority of workloads.
Practice Exercises
- You have a
productscollection queried almost exclusively bycategory, which has about 20 distinct values. Explain in your own words whycategoryalone would make a poor shard key, and propose a compound key that would work better. - Write the
sh.enableShardingandsh.shardCollectioncommands to shard areviewscollection in theecommercedatabase using a hashed key onproductId. - Run
db.reviews.getShardDistribution()against a cluster (or imagine the output) where one shard holds 95% of the data. What does that imbalance most likely indicate about the chosen shard key, and what would you check first?
Summary
- Sharding partitions a collection’s documents across multiple shards (each a replica set) to scale beyond one server’s capacity.
mongosroutes operations, config servers store the cluster’s chunk metadata, and shards store the actual data.- The shard key determines which shard a document lives on; it is chosen per collection and is very difficult to change later.
- Chunks are contiguous ranges of shard key values; the balancer migrates chunks to keep shards evenly loaded.
- Queries that include the shard key are targeted to specific shards; queries that omit it become expensive scatter-gather operations across every shard.
- Monotonically increasing keys and low-cardinality keys are the two most common shard key mistakes, both leading to hot, unbalanced shards.
- Sharding is for scaling past a real capacity ceiling, not a default architecture choice for a growing app.
