When the amount of data grows, a single server can no longer keep up — it reads slowly, writes slowly, and if it goes down, the application stops. MongoDB solves this with two mechanisms: replication keeps copies of the data on several servers (reliability), sharding splits a collection across servers (horizontal scaling). Let's look at both.
Why you can't just keep a single server
Imagine an online store with millions of products. With a single server:
- If the server goes down — the site is unavailable and sales stop.
- If there is more data than fits in RAM — every query goes to disk, and speed drops by tens of times.
- The write load is limited by the speed of a single disk.
Replication solves the first problem, sharding solves the second and third.
Replica set — real-time backups
A replica set is a group of MongoDB servers that store the same data. The minimal configuration is three nodes: one primary accepts all writes, two secondary nodes automatically copy the changes.
┌──────────┐
write → │ primary │ ──── replication ───┐
└──────────┘ │
▼
┌──────────┐ ┌──────────┐
read ──────────────────────── │secondary │ │secondary │
└──────────┘ └──────────┘
Why three and not two? Because during a failure the nodes vote on who becomes the new primary. A majority is required — with two servers, a single vote is not enough. Three nodes give you a quorum.
How the oplog works
The replication mechanism is built on the oplog — a special collection in the local database where the primary records every successful operation. Secondaries constantly read this log and replay the operations on themselves.
It's like an accounting ledger: the primary writes down "product X added", "price Y changed", and the secondaries "replay" these actions. If a secondary falls behind and reconnects — it catches up using the oplog.
One important point: the oplog has a fixed size (by default around 5% of free disk). If a secondary falls far behind and the entries it needs are no longer in the oplog — you have to copy the entire dataset again (initial sync). That's why under heavy load the oplog should be increased.
What happens when the primary goes down
The secondaries detect the loss of the primary within a few seconds and start voting. The winner is the one with the most up-to-date oplog. The whole procedure takes 10–30 seconds — during this time the cluster does not accept writes.
An important rule: if two of the three nodes are unavailable — the remaining one switches to read-only mode. This is protection against split brain: if both "surviving" segments kept accepting writes independently, the data would diverge.
Where read queries go
By default all queries go to the primary. But you can direct reads to a secondary — to offload the primary or to read from the geographically nearest node. This is called read preference:
| Mode | Where we read from | When to use |
|---|---|---|
primary (default) | Primary only | When you need the most up-to-date data |
secondary | Secondary only | Analytics, reports — don't load the primary |
secondaryPreferred | Secondary, primary if unavailable | Read load, data may lag slightly |
nearest | The node with minimal latency | Distributed clusters across regions |
A gotcha: a secondary may lag slightly behind the primary. If you just wrote data and read it immediately — use primary, otherwise you may read stale data.
db.product.find({ categoryId: 1 })
.readPref("secondaryPreferred");
When one replica set is no longer enough
A replica set solves the reliability problem, but not scaling. If there is so much data that it doesn't fit in the memory of a single server — queries start going to disk, and speed drops by tens of times.
A practical guideline: if the active dataset (the documents accessed most often) takes up more than 70% of RAM — it's time to think about the next step.
The next step in MongoDB is a sharded cluster: the data is physically split across several replica sets.
Sharded cluster — how it works inside
A sharded cluster has four types of components:
client → mongos (router) → Shard A (replica set)
→ Shard B (replica set)
→ Shard C (replica set)
↕
Config Servers (replica set of 3 nodes)
- mongos — the entry point. The client connects to mongos without knowing about the shards. Mongos looks at the metadata and routes the query to the right shard. You can run several mongos instances — it stores no data and works as a proxy.
- Config servers — three nodes that store the map: which data is on which shard. Without them the cluster does not work.
- Shards — ordinary replica sets, each storing its own portion of the data.
- Balancer — a background process that watches for evenness. If one shard is overloaded — it moves part of the data to another.
Data inside a collection is divided into chunks (pieces of 128 MB). The balancer moves chunks between shards so that the load stays roughly equal.
It's important to understand the cost: a minimal production cluster is 3 config server nodes + 2 shards of 3 replicas each + 2 mongos = 11 servers. That's significantly more expensive than a single replica set. Sharding is enabled only when there's really no way around it.
Shard key — the most important decision in sharding
A shard key is a field (or several fields) by which MongoDB decides which shard to put a document on. This decision is made once and is hard to change — so choosing a shard key deserves attention.
Even distribution versus a hot shard
Imagine: a store shards its product collection by the _id field of type ObjectId. ObjectId grows monotonically over time — all new products land on the same shard. It's overloaded, the rest sit idle. This is called a hot shard — the main mistake in sharding.
Ranged sharding — documents are distributed by ranges of values. Suitable when values are evenly distributed or when queries often filter by a range (for example, by date):
sh.shardCollection("shop.product", { categoryId: 1 });
Hashed sharding — MongoDB hashes the value itself, and the distribution is always even. Good for monotonic keys like ObjectId or timestamp. Downside: range queries are forced to poll all shards:
sh.shardCollection("shop.product", { _id: "hashed" });
Compound shard key
Often the best option is a compound key: the first part provides variety (evenness), the second provides locality for typical queries.
If queries usually go by categoryId, and each category has a different number of products:
sh.shardCollection("shop.product", { categoryId: 1, _id: "hashed" });
Products of one category are hashed by _id and distributed evenly across shards. At the same time, queries by categoryId hit fewer shards than with pure hashing by _id.
Zoned sharding — data in the right region
If regulation requires storing users' data in a specific country — zoned sharding is used: ranges of shard key values are assigned tags, and shards are assigned the same tags:
sh.addShardTag("shardEU", "EU");
sh.addShardTag("shardUS", "US");
sh.addTagRange(
"shop.product",
{ region: "EU", productId: MinKey },
{ region: "EU", productId: MaxKey },
"EU"
);
Data with region: "EU" will physically stay on European servers.
How to choose a good shard key
Five criteria to check:
- High cardinality — many distinct values. A
statusfield with three values won't let you distribute data across ten shards. - Even distribution — otherwise a hot shard.
userIdis usually good,countryfor a single country is bad. - Present in most queries — otherwise every query polls all shards (scatter-gather), which is slow.
- Rarely changes — the shard key is practically immutable. Changing it is possible since 5.0 via
reshardCollection, but it's a long operation. - Writes hit a single shard — each write operation lands on a specific shard without distributed transactions.
A practical tip: if the product collection is sharded but the category collection is not — queries with $lookup between them are forced to poll all shards. It's better to shard both collections by categoryId: then related documents end up physically close and the join stays local.
In short
- Replica set — three or more nodes storing the same data. The primary accepts writes, secondaries replicate through the oplog.
- When the primary goes down, the nodes vote and elect a new one — 10–30 seconds without writes. This is normal.
- A write with
w: "majority"survives failover; withw: 1— it doesn't. - The oplog is an operations log; if a secondary falls beyond its boundary, a full initial sync is needed.
- Reads can be directed to a secondary via
readPreference— to offload the primary or read from the nearest node. - A sharded cluster is needed when the active data doesn't fit in the memory of a single server. Minimum 11 nodes — expensive.
- The shard key is the key decision: high cardinality, evenness, presence in queries, rare changes.
- A monotonic key without hashing creates a hot shard — all writes go to one shard. Solution:
hashedsharding. - A compound shard key is often better than a single field: evenness + query locality.
What to read next
- ACID and consistency in MongoDB — which guarantees hold in a sharded cluster and how to use causal consistency.
- Document modeling — the right schema reduces load and delays the need for sharding.