Chapter 78 โ Replication & Sharding
๐ Definitions
- Replication โ keeping identical copies of data on multiple servers (a replica set) for high availability and data safety.
- Sharding โ splitting one collection's data across multiple replica sets (shards) for horizontal scaling of storage and write throughput.
Replication = copies of the same data (availability). Sharding = different pieces of data on different machines (scale). Production clusters use both: every shard is a replica set.
๐ Replica Set Architecture
writes + default reads
App โโโโโโโโโโโโโโโโโโโโโโโโโโโโบ PRIMARY
โ oplog (operations log)
โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโ
โผ โผ
SECONDARY SECONDARY
(copies the oplog, (can serve reads if
applies operations) readPreference allows)
heartbeat every 2s between all members; if primary unreachable for
electionTimeoutMillis (10s default) โ election โ a secondary becomes primary| Concept | Detail |
|---|---|
| Primary | Only member that accepts writes |
| Secondary | Replicates the primary's oplog asynchronously; can serve reads |
| Oplog | Capped collection local.oplog.rs of idempotent operations; secondaries tail it |
| Arbiter | Votes in elections, holds no data (avoid in production when possible โ weakens majority writes) |
| Hidden / delayed member | Not visible to apps; delayed member keeps an older copy (e.g. 1h) to recover from accidental deletes |
| Size | Up to 50 members, max 7 voting; use an odd number of voters (3 or 5) |
๐ป Setting Up a Replica Set
// Start 3 mongod processes with --replSet rs0, then in mongosh:
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "mongo1:27017", priority: 2 }, // preferred primary
{ _id: 1, host: "mongo2:27017", priority: 1 },
{ _id: 2, host: "mongo3:27017", priority: 1 },
],
});
rs.status(); // health, state, optime of each member
rs.printSecondaryReplicationInfo(); // replication lag
rs.stepDown(); // force primary to step down (maintenance)Connection string lists several hosts; the driver discovers the topology and follows the primary after failover:
mongodb://mongo1:27017,mongo2:27017,mongo3:27017/app?replicaSet=rs0&retryWrites=true&w=majority๐ Elections & Failover
- Secondaries stop receiving heartbeats from the primary for
electionTimeoutMillis(10s). - An eligible secondary calls an election; members vote (majority of voting members required).
- The most up-to-date, highest-priority eligible member typically wins.
- Drivers detect the new primary; retryable writes retry the failed write.
- Typical failover: ~10โ12 seconds of no writes.
Why majority? It prevents split brain: during a network partition only the side with a majority can elect a primary. With 3 nodes, 1 can fail. With 5, 2 can fail. With 4 โ still only 1 (that's why odd numbers).
Rollback: writes acknowledged with w: 1 on an old primary that hadn't replicated them are rolled back after failover. w: "majority" writes are never rolled back.
๐ Sharded Cluster Architecture
App
โ
โผ
mongos (query router โ stateless, run several)
โ reads chunk โ shard map from config servers
โโโโโโโโโโผโโโโโโโโโฌโโโโโโโโโโโโ
โผ โผ โผ โผ
Shard A Shard B Shard C Config servers (replica set):
(replica (replica (replica metadata โ which ranges live where
set) set) set)| Component | Role |
|---|---|
| mongos | Router: sends each query to the right shard(s), merges results |
| Config servers | Replica set storing cluster metadata and chunk ranges |
| Shard | A replica set holding a subset of the data |
| Chunk | A contiguous range of shard-key values (default max 128 MB since 6.0) |
| Balancer | Background process that moves chunks to even out shards |
๐ป Sharding a Collection
sh.enableSharding("shop"); // optional on 6.0+; implicit
db.orders.createIndex({ customerId: 1, orderDate: 1 });
sh.shardCollection("shop.orders", { customerId: 1, orderDate: 1 }); // ranged sharding
sh.shardCollection("shop.events", { deviceId: "hashed" }); // hashed sharding
sh.status(); // distribution of chunks
db.orders.getShardDistribution();๐ Choosing a Shard Key (The Most Important Decision)
A good shard key has:
| Property | Why | Bad example |
|---|---|---|
| High cardinality | Many distinct values โ can split into many chunks | status (5 values) |
| Low frequency | No single value dominates | country where 80% are "IN" |
| Non-monotonic | Inserts spread across shards | createdAt, ObjectId โ all inserts hit the last chunk ("hot shard") |
| Matches queries | Queries including the key are targeted to one shard | Key never used in filters โ every query is scatter-gather |
| Strategy | Pros | Cons |
|---|---|---|
Ranged { customerId: 1 } |
Range queries hit few shards | Monotonic keys create hot spots |
Hashed { _id: "hashed" } |
Even write distribution | Range queries go to all shards |
Compound { tenantId: 1, _id: 1 } |
Targeted per tenant + good spread | Must design with query patterns |
| Zoned | Pin ranges to regions (e.g. EU data in EU shards) | Extra configuration |
Since 5.0 you can reshard (reshardCollection) and since 4.4 refine a key by adding suffix fields โ but changing the key is still expensive, so choose carefully.
๐ Targeted vs Scatter-Gather Queries
// Shard key: { customerId: 1, orderDate: 1 }
db.orders.find({ customerId: 42 }); // โ
targeted โ one shard
db.orders.find({ customerId: 42, orderDate: { $gte: d } }); // โ
targeted
db.orders.find({ status: "paid" }); // โ scatter-gather โ every shardScatter-gather isn't wrong, but its latency is the slowest shard's latency and it doesn't scale with more shards.
โ ๏ธ Sharding Gotchas
- Jumbo chunks: too many docs with the same shard-key value can't be split.
- Unique indexes must include the shard key as a prefix (only
_iduniqueness is per-shard otherwise). - Don't shard too early: vertical scaling + indexes + replica set reads often suffice; shard when data or write volume exceeds one replica set (often > 1โ2 TB or very high write rates).
- Transactions across shards work (4.2+) but are slower.
๐ Replication vs Sharding
| Replication | Sharding | |
|---|---|---|
| Goal | Availability, durability, read scaling | Write + storage scaling |
| Data on each node | Full copy | Subset |
| Minimum setup | 3 members | Config RS + โฅ2 shard RS + mongos |
| Complexity | Low | High |
๐ Real-World Impact
- A 3-node replica set across availability zones survives a data-centre outage with ~10s of write unavailability and no data loss (with
w: majority). - A team sharded on
createdAtand found one shard took 100% of inserts; resharding to{ tenantId: 1, createdAt: 1 }spread the load. - A delayed secondary recovered a collection someone dropped by mistake.
๐ฏ Likely Interview Questions
- What is a replica set? โ A group of
mongodnodes with the same data: one primary, multiple secondaries, automatic failover. - How does replication work? โ Secondaries tail and apply the primary's oplog asynchronously.
- What happens when the primary goes down? โ Heartbeats fail โ election โ a secondary becomes primary (~10s); drivers reconnect; retryable writes retry.
- Why should a replica set have an odd number of voting members? โ A majority is needed to elect; odd numbers maximise fault tolerance and avoid ties.
- What is an arbiter? โ A voting member without data; cheap tie-breaker but weakens majority write durability.
- What is sharding and when do you need it? โ Horizontal partitioning across shards when one replica set can't hold the data or handle the writes.
- What are the components of a sharded cluster? โ
mongosrouters, config server replica set, shard replica sets. - How do you choose a shard key? โ High cardinality, low frequency, non-monotonic, used in most queries.
- Hashed vs ranged sharding? โ Even distribution vs efficient range queries.
- Why is
createdAtorObjectIda bad shard key? โ Monotonic โ all inserts hit one shard. - What is a scatter-gather query? โ A query without the shard key, sent to all shards.
- Replication vs sharding? โ Copies for availability vs partitions for scale.
- What is a rollback in a replica set? โ Undoing writes on a former primary that weren't replicated to a majority before failover.
โ Performance Tuning | Index | Next: Advanced Features โ