Skip to content

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 27019
js
// 连接一台并初始化
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: 1Primary 确认即可
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)
不可变分片键一旦设定不可修改

参考