第七章:复制集¶
复制集概述¶
复制集(Replica Set)是 MongoDB 实现高可用的核心机制,通过数据冗余和自动故障转移保证服务可用性。
架构图¶
┌─────────────────────────────────────────────────────────────┐
│ Replica Set │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Primary │───▶│ Secondary │───▶│ Secondary │ │
│ │ (主节点) │◀───│ (从节点) │◀───│ (从节点) │ │
│ │ Port: 27017│ │ Port: 27018│ │ Port: 27019│ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ └──────────────────┴──────────────────┘ │
│ 数据复制 │
│ │
│ ┌─────────────┐ │
│ │ Arbiter │ ← 仲裁节点(可选) │
│ │ (投票用) │ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
节点角色¶
| 角色 | 说明 |
|---|---|
| Primary | 主节点,处理所有写操作 |
| Secondary | 从节点,复制主节点数据 |
| Arbiter | 仲裁节点,只投票不存储数据 |
| Hidden | 隐藏节点,对客户端不可见 |
| Delayed | 延迟节点,延迟复制数据 |
部署复制集¶
单机多实例部署¶
# 创建数据目录
mkdir -p /data/rs0-0 /data/rs0-1 /data/rs0-2
# 启动三个实例
mongod --replSet rs0 --port 27017 --dbpath /data/rs0-0 --bind_ip localhost
mongod --replSet rs0 --port 27018 --dbpath /data/rs0-1 --bind_ip localhost
mongod --replSet rs0 --port 27019 --dbpath /data/rs0-2 --bind_ip localhost
初始化复制集¶
// 连接到主节点
mongosh --port 27017
// 初始化配置
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "localhost:27017" },
{ _id: 1, host: "localhost:27018" },
{ _id: 2, host: "localhost:27019" }
]
})
// 查看状态
rs.status()
配置文件方式¶
# /etc/mongod.conf (节点1)
storage:
dbPath: /data/rs0-0
net:
port: 27017
bindIp: localhost
replication:
replSetName: rs0
oplogSizeMB: 1024
security:
keyFile: /etc/mongodb/keyfile
Docker Compose 部署¶
version: '3.8'
services:
mongo1:
image: mongo:7.0
container_name: mongo1
ports:
- "27017:27017"
command: mongod --replSet rs0
volumes:
- mongo1_data:/data/db
mongo2:
image: mongo:7.0
container_name: mongo2
ports:
- "27018:27017"
command: mongod --replSet rs0
volumes:
- mongo2_data:/data/db
mongo3:
image: mongo:7.0
container_name: mongo3
ports:
- "27019:27017"
command: mongod --replSet rs0
volumes:
- mongo3_data:/data/db
volumes:
mongo1_data:
mongo2_data:
mongo3_data:
复制集管理¶
查看状态¶
// 复制集状态
rs.status()
// 简洁输出
rs.status().members.forEach(m => print(m.name, m.stateStr))
// 查看配置
rs.conf()
// 查看是否为主节点
rs.isMaster()
添加/删除节点¶
// 添加节点
rs.add("host4:27017")
// 添加仲裁节点
rs.addArb("arbiter:27017")
// 添加隐藏节点
rs.add({
host: "hidden:27017",
priority: 0,
hidden: true
})
// 删除节点
rs.remove("host4:27017")
修改配置¶
// 获取当前配置
var cfg = rs.conf()
// 修改优先级
cfg.members[0].priority = 2
cfg.members[1].priority = 1
// 重新配置
rs.reconfig(cfg)
// 强制重新配置(无主节点时)
rs.reconfig(cfg, { force: true })
主节点切换¶
// 步降主节点
rs.stepDown(60) // 60 秒内不能成为主节点
// 冻结节点
rs.freeze(120) // 120 秒内不能参与选举
// 强制指定主节点
cfg = rs.conf()
cfg.members[1].priority = 10 // 提高优先级
rs.reconfig(cfg)
数据复制机制¶
Oplog¶
Oplog(操作日志)是复制集的核心:
// 查看 oplog 大小
db.printReplicationInfo()
// 查看 oplog 内容
use local
db.oplog.rs.find().sort({$natural: -1}).limit(5)
// oplog 结构
{
"ts": Timestamp(1704067200, 1), // 时间戳
"t": NumberLong(1), // term
"h": NumberLong("123456789"), // hash
"v": 2, // 版本
"op": "i", // 操作类型: i=insert, u=update, d=delete
"ns": "mydb.users", // 命名空间
"o": { ... } // 操作文档
}
复制过程¶
Primary Secondary
│ │
│ 1. 写入 Oplog │
│──────▶ │
│ │
│ 2. 返回写入确认 │
│◀────── │
│ │
│ 3. Secondary 轮询 Oplog │
│◀────────────────────────────────│
│ │
│ 4. 返回新操作 │
│────────────────────────────────▶│
│ │
│ 5. 应用操作 │
│ │
复制延迟¶
// 查看复制延迟
rs.printSlaveReplicationInfo()
// 输出示例
// source: host2:27017
// syncedTo: Fri Jan 01 2024 10:00:00 GMT+0800
// 0 secs (0 hrs) behind the primary
选举机制¶
选举触发条件¶
- 复制集初始化
- 主节点不可用
- 主节点步降
- 新节点加入
选举过程¶
投票规则¶
- 需要获得多数票(N/2 + 1)
- 奇数节点最佳(3、5、7)
- 仲裁节点只投票不存储数据
优先级¶
// 设置优先级
cfg = rs.conf()
cfg.members[0].priority = 5 // 更可能成为主节点
cfg.members[1].priority = 1
cfg.members[2].priority = 0 // 永远不会成为主节点
rs.reconfig(cfg)
读偏好¶
读偏好模式¶
| 模式 | 说明 |
|---|---|
| primary | 只从主节点读(默认) |
| primaryPreferred | 优先主节点,不可用时读从节点 |
| secondary | 只从从节点读 |
| secondaryPreferred | 优先从节点,无可用时读主节点 |
| nearest | 读网络延迟最低的节点 |
连接设置¶
// 连接字符串
mongodb://host1:27017,host2:27017,host3:27017/?readPreference=secondaryPreferred
// 驱动设置
const client = new MongoClient(uri, {
readPreference: 'secondaryPreferred'
});
查询级别¶
// 查询时指定
db.users.find({}).readPref('secondary')
// 聚合时指定
db.orders.aggregate([...]).readPref('secondary')
写关注¶
写关注级别¶
// w: 1 - 默认,主节点确认
db.users.insertOne({...}, { w: 1 })
// w: "majority" - 多数节点确认
db.users.insertOne({...}, { w: "majority" })
// w: 2 - 指定节点数确认
db.users.insertOne({...}, { w: 2 })
// w: "customTag" - 指定标签节点确认
db.users.insertOne({...}, { w: "eastCoast" })
// j: true - 等待日志落盘
db.users.insertOne({...}, { w: "majority", j: true })
// wtimeout - 超时时间
db.users.insertOne({...}, {
w: "majority",
wtimeout: 5000
})
故障转移¶
自动故障转移¶
手动故障转移¶
安全配置¶
Keyfile 认证¶
# 生成 keyfile
openssl rand -base64 756 > /etc/mongodb/keyfile
chmod 400 /etc/mongodb/keyfile
chown mongodb:mongodb /etc/mongodb/keyfile
用户创建¶
// 创建管理员
use admin
db.createUser({
user: "admin",
pwd: "StrongPassword123!",
roles: [
{ role: "userAdminAnyDatabase", db: "admin" },
{ role: "clusterAdmin", db: "admin" }
]
})
// 创建应用用户
use mydb
db.createUser({
user: "appuser",
pwd: "AppPassword123!",
roles: [
{ role: "readWrite", db: "mydb" }
]
})
监控与维护¶
监控指标¶
// 复制集状态
rs.status()
// 复制延迟
rs.printSlaveReplicationInfo()
// 服务器状态
db.serverStatus().repl
// Oplog 状态
db.printReplicationInfo()
常见问题¶
复制延迟过大¶
主节点频繁切换¶
小结¶
本章学习了:
- ✅ 复制集架构和节点角色
- ✅ 部署复制集
- ✅ 复制集管理
- ✅ 数据复制机制
- ✅ 选举机制
- ✅ 读偏好和写关注
- ✅ 故障转移
- ✅ 安全配置
下一章¶
第八章:分片集群 - 学习 MongoDB 大规模分片集群架构。