本文档介绍 MongoDB 的存储引擎、副本集和分片集群架构。
MongoDB 默认使用 WiredTiger 存储引擎(3.2+),提供:
# mongod.conf 配置
storage:
engine: wiredTiger
wiredTiger:
engineConfig:
cacheSizeGB: 4 # 缓存大小
journalCompressor: snappy
collectionConfig:
blockCompressor: snappy
indexConfig:
prefixCompression: true副本集是一组维护相同数据集的 MongoDB 实例,提供数据冗余和高可用性。
┌─────────────────────────────────────┐
│ 副本集架构 │
└─────────────────────────────────────┘
│
┌───────────────────────────┼───────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Primary │───────▶│ Secondary │───────▶│ Secondary │
│ (主节点) │ │ (从节点) │ │ (从节点) │
│ 读写操作 │ │ 只读 │ │ 只读 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
│ 异步复制 │
└───────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Arbiter (仲裁节点) │
│ 不存储数据,仅参与投票,节省资源 │
└─────────────────────────────────────────────────────────────────┘| 角色 | 描述 | 功能 |
|---|---|---|
| Primary | 主节点 | 处理所有写操作,默认读操作 |
| Secondary | 从节点 | 复制主节点数据,可处理读请求 |
| Arbiter | 仲裁节点 | 参与选举投票,不存储数据 |
| Hidden | 隐藏节点 | 对客户端不可见,用于备份/报表 |
| Delayed | 延迟节点 | 数据延迟同步,用于灾难恢复 |
// 初始化副本集
rs.initiate({
_id: "myReplicaSet",
members: [
{ _id: 0, host: "mongo1:27017", priority: 2 },
{ _id: 1, host: "mongo2:27017", priority: 1 },
{ _id: 2, host: "mongo3:27017", priority: 1 },
{ _id: 3, host: "mongo4:27017", arbiterOnly: true }
]
})
// 查看副本集状态
rs.status()
// 查看配置
rs.conf()
// 添加成员
rs.add("mongo5:27017")
// 添加仲裁节点
rs.addArb("mongo6:27017")
// 移除成员
rs.remove("mongo5:27017")
// 配置隐藏节点
rs.add({
host: "mongo-hidden:27017",
priority: 0,
hidden: true
})
// 配置延迟节点
rs.add({
host: "mongo-delayed:27017",
priority: 0,
hidden: true,
secondaryDelaySeconds: 3600 // 延迟 1 小时(旧版为 slaveDelay,已弃用)
})// 写关注(Write Concern)
db.orders.insertOne(
{ item: "book", qty: 10 },
{ writeConcern: { w: "majority", j: true, wtimeout: 5000 } }
)
// w: "majority" - 大多数节点确认
// j: true - 等待 journal 刷盘
// wtimeout: 超时时间
// 读关注(Read Concern)
db.orders.find().readConcern("majority")
// 读偏好(Read Preference)
// primary - 只从主节点读(默认)
// primaryPreferred - 优先主节点
// secondary - 只从从节点读
// secondaryPreferred - 优先从节点
// nearest - 最近节点
db.orders.find().readPref("secondaryPreferred")// Node.js 连接副本集
const uri = "mongodb://mongo1:27017,mongo2:27017,mongo3:27017/mydb?replicaSet=myReplicaSet"
const client = new MongoClient(uri, {
readPreference: "secondaryPreferred",
readConcernLevel: "majority",
w: "majority"
})故障转移流程:
1. 主节点故障
2. 从节点检测心跳超时
3. 发起选举
4. 获得多数票的从节点成为新主节点
5. 客户端自动重连到新主节点
选举条件:
- 需要获得多数成员投票
- 至少需要 3 个成员才能自动故障转移
- 优先级最高的节点优先成为主节点分片允许将数据分布在多个服务器上,实现水平扩展。
┌─────────────────────────────────────────────────────────────────────────┐
│ 分片集群架构 │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ mongos (路由节点) │
│ 接收查询,路由到正确的分片 │
└─────────────────────────────────────────────────────────────────────────┘
│
│ 查询路由
│
┌──────────────────────────┼──────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Shard 1 │ │ Shard 2 │ │ Shard 3 │
│ (副本集) │ │ (副本集) │ │ (副本集) │
│ 范围: min-100 │ │ 范围: 100-200 │ │ 范围: 200-max │
└─────────────────┘ └─────────────────┘ └─────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ Config Servers (配置服务器) │
│ 存储集群元数据和配置信息 │
│ (必须是副本集) │
└─────────────────────────────────────────────────────────────────────────┘| 组件 | 数量 | 描述 |
|---|---|---|
| mongos | 1+ | 路由进程,客户端连接入口 |
| Config Servers | 3 | 存储集群元数据,必须是副本集 |
| Shard | 1+ | 存储数据,推荐使用副本集 |
| 策略 | 描述 | 适用场景 |
|---|---|---|
| 范围分片 | 按分片键值范围划分 | 范围查询频繁 |
| 哈希分片 | 按分片键哈希值划分 | 数据均匀分布,随机访问 |
| 区域分片 | 自定义数据分布规则(旧称标签分片) | 地理分布、多租户 |
# 1. 启动配置服务器(必须是副本集)
mongod --configsvr --replSet configRs --port 27019
# 初始化配置服务器副本集
mongosh --port 27019
rs.initiate({ _id: "configRs", configsvr: true, members: [...] })# 2. 启动 mongos 路由
mongos --configdb configRs/mongo-cfg1:27019,mongo-cfg2:27019,mongo-cfg3:27019// 3. 添加分片
sh.addShard("shard1/mongo-s1a:27017,mongo-s1b:27017")
sh.addShard("shard2/mongo-s2a:27017,mongo-s2b:27017")
// 4. 启用数据库分片
sh.enableSharding("mydb")
// 5. 对集合分片
// 范围分片
sh.shardCollection("mydb.users", { username: 1 })
// 哈希分片
sh.shardCollection("mydb.logs", { timestamp: "hashed" })
// 查看分片状态
sh.status()
// 查看分片分布
db.users.getShardDistribution()// 将分片划入区域(旧版 addShardTag/addTagRange 已移除)
sh.addShardToZone("shard1", "US")
sh.addShardToZone("shard2", "EU")
sh.addShardToZone("shard3", "APAC")
// 配置区域对应的分片键范围(左闭右开区间)
sh.updateZoneKeyRange("mydb.users", { region: "US" }, { region: "US\uffff" }, "US")
sh.updateZoneKeyRange("mydb.users", { region: "EU" }, { region: "EU\uffff" }, "EU")
// 启用均衡器
sh.enableBalancing("mydb.users")
sh.startBalancer()好的分片键示例:
- { userId: 1 } - 高基数,常用查询
- { _id: "hashed" } - 均匀分布
- { createdAt: 1, userId: 1 } - 复合分片键
不好的分片键示例:
- { timestamp: 1 } - 单调递增,产生热点
- { status: 1 } - 低基数,数据倾斜// 查看均衡状态
sh.getBalancerState()
// 手动迁移数据段(5.0+ 推荐 moveRange,替代已弃用的 moveChunk)
sh.moveRange("mydb.users", { username: "a" }, { username: "b" }, "shard2")
// 拆分块
sh.splitAt("mydb.users", { username: "m" })
// 关闭均衡器(维护期间)
sh.stopBalancer()
sh.startBalancer()上一篇:Node.js 操作
下一篇:运维管理