{T}

MongoDB 架构与高级特性

本文档介绍 MongoDB 的存储引擎、副本集和分片集群架构。

存储引擎

MongoDB 默认使用 WiredTiger 存储引擎(3.2+),提供:

  • 文档级锁:高并发写入性能
  • 压缩:支持 snappy 和 zlib 压缩
  • 检查点:定期将数据持久化到磁盘
  • Journaling:写操作日志,保证数据安全
yaml
# mongod.conf 配置
storage:
  engine: wiredTiger
  wiredTiger:
    engineConfig:
      cacheSizeGB: 4          # 缓存大小
      journalCompressor: snappy
    collectionConfig:
      blockCompressor: snappy
    indexConfig:
      prefixCompression: true

副本集(Replica Set)

副本集是一组维护相同数据集的 MongoDB 实例,提供数据冗余和高可用性。

架构图

code
                    ┌─────────────────────────────────────┐
                    │            副本集架构                │
                    └─────────────────────────────────────┘
                                     │
         ┌───────────────────────────┼───────────────────────────┐
         │                           │                           │
         ▼                           ▼                           ▼
┌─────────────────┐        ┌─────────────────┐        ┌─────────────────┐
│    Primary      │───────▶│   Secondary     │───────▶│   Secondary     │
│   (主节点)       │        │   (从节点)       │        │   (从节点)       │
│   读写操作       │        │   只读          │        │   只读          │
└─────────────────┘        └─────────────────┘        └─────────────────┘
         │                           │
         │     异步复制               │
         └───────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│                       Arbiter (仲裁节点)                         │
│   不存储数据,仅参与投票,节省资源                                  │
└─────────────────────────────────────────────────────────────────┘

副本集成员角色

角色描述功能
Primary主节点处理所有写操作,默认读操作
Secondary从节点复制主节点数据,可处理读请求
Arbiter仲裁节点参与选举投票,不存储数据
Hidden隐藏节点对客户端不可见,用于备份/报表
Delayed延迟节点数据延迟同步,用于灾难恢复

副本集配置

javascript
// 初始化副本集
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,
  slaveDelay: 3600  // 延迟 1 小时
})

读写关注

javascript
// 写关注(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")

连接副本集

javascript
// 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"
})

故障转移

code
故障转移流程:
1. 主节点故障
2. 从节点检测心跳超时
3. 发起选举
4. 获得多数票的从节点成为新主节点
5. 客户端自动重连到新主节点

选举条件:
- 需要获得多数成员投票
- 至少需要 3 个成员才能自动故障转移
- 优先级最高的节点优先成为主节点

分片集群(Sharding)

分片允许将数据分布在多个服务器上,实现水平扩展。

架构图

code
┌─────────────────────────────────────────────────────────────────────────┐
│                           分片集群架构                                    │
└─────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────┐
│                          mongos (路由节点)                               │
│                     接收查询,路由到正确的分片                             │
└─────────────────────────────────────────────────────────────────────────┘
                                    │
                                    │ 查询路由
                                    │
         ┌──────────────────────────┼──────────────────────────┐
         │                          │                          │
         ▼                          ▼                          ▼
┌─────────────────┐        ┌─────────────────┐        ┌─────────────────┐
│    Shard 1      │        │    Shard 2      │        │    Shard 3      │
│  (副本集)        │        │  (副本集)        │        │  (副本集)        │
│  范围: min-100  │        │  范围: 100-200  │        │  范围: 200-max  │
└─────────────────┘        └─────────────────┘        └─────────────────┘

┌─────────────────────────────────────────────────────────────────────────┐
│                      Config Servers (配置服务器)                          │
│                    存储集群元数据和配置信息                                │
│                        (必须是副本集)                                     │
└─────────────────────────────────────────────────────────────────────────┘

组件说明

组件数量描述
mongos1+路由进程,客户端连接入口
Config Servers3存储集群元数据,必须是副本集
Shard1+存储数据,推荐使用副本集

分片策略

策略描述适用场景
范围分片按分片键值范围划分范围查询频繁
哈希分片按分片键哈希值划分数据均匀分布,随机访问
标签分片自定义数据分布规则地理分布、多租户

配置分片

bash
# 1. 启动配置服务器(必须是副本集)
mongod --configsvr --replSet configRs --port 27019

# 初始化配置服务器副本集
mongosh --port 27019
rs.initiate({ _id: "configRs", configsvr: true, members: [...] })
bash
# 2. 启动 mongos 路由
mongos --configdb configRs/mongo-cfg1:27019,mongo-cfg2:27019,mongo-cfg3:27019
javascript
// 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()

标签分片

javascript
// 添加标签
sh.addShardTag("shard1", "US")
sh.addShardTag("shard2", "EU")
sh.addShardTag("shard3", "APAC")

// 配置标签范围
sh.addTagRange(
  "mydb.users",
  { region: "US" },
  { region: "US" },
  "US"
)

// 启用均衡器
sh.enableBalancing("mydb.users")
sh.startBalancer()

分片键选择原则

  • 高基数:分片键值多样性高,避免数据倾斜
  • 高频率查询:选择常用查询字段
  • 非单调递增:避免热点问题
  • 避免低基数:如性别、状态等字段
code
好的分片键示例:
- { userId: 1 } - 高基数,常用查询
- { _id: "hashed" } - 均匀分布
- { createdAt: 1, userId: 1 } - 复合分片键

不好的分片键示例:
- { timestamp: 1 } - 单调递增,产生热点
- { status: 1 } - 低基数,数据倾斜

数据均衡

javascript
// 查看均衡状态
sh.getBalancerState()

// 手动迁移块
sh.moveChunk("mydb.users", { username: "alice" }, "shard2")

// 拆分块
sh.splitAt("mydb.users", { username: "m" })

// 关闭均衡器(维护期间)
sh.stopBalancer()
sh.startBalancer()

性能考量

副本集性能

  • 写操作需要复制到多数节点
  • 从节点读取可能存在延迟
  • 合理配置 priority 影响选举

分片集群性能

  • 分片键决定数据分布
  • 广播查询(无分片键)需要查询所有分片
  • 跨分片事务性能较低

上一篇:[Node.js 操作](3-Node.js 操作.md)
下一篇运维管理