MongoDB 副本集与分片
副本集(Replica Set)
副本集是一组维护相同数据集的 mongod 进程,提供冗余和高可用。
架构
┌─────────────────────────────┐
│ Replica Set │
│ ┌─────────┐ ┌──────────┐ │
│ │ Primary │──▶│Secondary1│ │ write concern: majority
│ └─────────┘ └──────────┘ │
│ │ │
│ └──────▶┌──────────┐ │
│ │Secondary2│ │ heartbeat every 2s
│ └──────────┘ │
└─────────────────────────────┘| 角色 | 职责 |
|---|---|
| Primary | 唯一接收写操作,以 oplog 记录所有变更 |
| Secondary | 异步复制 Primary 的 oplog,可处理读请求 |
| Arbiter | 仅参与选举投票,不存储数据(用于突破奇数节点限制) |
副本集最少 3 个节点(或 2 节点 + 1 仲裁者),选举需要多数票。
配置副本集
shell
# 启动 3 个 mongod(不同端口 / 数据目录)
mongod --replSet rs0 --dbpath /data/rs0-0 --port 27017
mongod --replSet rs0 --dbpath /data/rs0-1 --port 27018
mongod --replSet rs0 --dbpath /data/rs0-2 --port 27019js
// 连接一台并初始化
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "localhost:27017", priority: 2 },
{ _id: 1, host: "localhost:27018" },
{ _id: 2, host: "localhost:27019" }
]
})
// 查看状态
rs.status()
rs.conf()
// 添加 / 删除成员
rs.add("localhost:27020")
rs.remove("localhost:27019")
// 手动降级 Primary
rs.stepDown()读写关注(Read / Write Concern)
js
// writeConcern — 写确认级别
db.collection.insertOne(
{ name: "test" },
{ writeConcern: { w: "majority", wtimeout: 1000 } }
)
// readConcern — 读隔离级别
db.collection.find().readConcern("majority")
// readPreference — 从哪里读
db.collection.find().readPref("secondaryPreferred")| writeConcern | 含义 |
|---|---|
w: 1 | Primary 确认即可 |
w: "majority" | 多数节点确认 |
w: 3 | 指定节点数确认 |
分片集群(Sharded Cluster)
分片将数据水平拆分到多台机器,突破单机容量和性能瓶颈。
架构
┌─────────┐
│ mongos │ 路由层(客户端入口)
└─────────┘
│
┌──────────┼──────────┐
│ │ │
┌───────┐ ┌───────┐ ┌──────────┐
│Config │ │Config │ │ Config │ 配置服务器副本集(元数据)
│Server1│ │Server2│ │ Server3 │
└───────┘ └───────┘ └──────────┘
│ │ │
┌───────┐ ┌───────┐ ┌───────┐
│Shard 1│ │Shard 2│ │Shard 3│ 每个 Shard 是一个副本集
│ RS │ │ RS │ │ RS │
└───────┘ └───────┘ └───────┘| 组件 | 作用 |
|---|---|
| mongos | 查询路由,客户端通过它访问集群 |
| Config Server | 存储集群元数据(数据分布),必须为副本集 |
| Shard | 实际数据存储,每个 Shard 是一个副本集 |
部署分片集群
shell
# 1. 启动 Config Server 副本集(3 节点)
mongod --configsvr --replSet configRS --dbpath /data/cfg0 --port 27019
# ... cfg1:27020, cfg2:27021
# 2. 初始化 Config Server 副本集
# rs.initiate({ _id: "configRS", configsvr: true, members: [...] })
# 3. 启动 mongos 路由
mongos --configdb configRS/localhost:27019,localhost:27020,localhost:27021 \
--port 27017
# 4. 启动 Shard 副本集
mongod --shardsvr --replSet shard1 --dbpath /data/s1 --port 27022
# ... 每个 shard 一个副本集
# 5. 添加 Shard
mongosh --port 27017
sh.addShard("shard1/localhost:27022,localhost:27023,localhost:27024")
sh.addShard("shard2/localhost:27025,localhost:27026,localhost:27027")分片策略
js
// 对数据库启用分片
sh.enableSharding("myDatabase")
// 哈希分片 — 数据均匀分布
sh.shardCollection("myDatabase.users", { userId: "hashed" })
// 范围分片 — 按范围路由(适合范围查询)
sh.shardCollection("myDatabase.orders", { region: 1, orderId: 1 })
// 分片区(Zone)— 将特定范围的数据定位到指定 Shard
sh.addShardToZone("shard1", "ASIA")
sh.updateZoneKeyRange("myDatabase.users",
{ region: "Asia" }, { region: "Asia" },
"ASIA"
)
// 查看状态
sh.status()分片键选择原则
| 原则 | 说明 |
|---|---|
| 高基数 | 避免单 chunk 数据过大 |
| 均匀分布 | 避免热点 Shard |
| 覆盖查询 | 尽量让查询命中单个 Shard(Targeted Query) |
| 不可变 | 分片键一旦设定不可修改 |