Appearance
副本集
副本集概述
什么是副本集?
副本集(Replica Set)是一组 MongoDB 实例,提供数据冗余和高可用性。
副本集架构
┌─────────────┐
│ Primary │
│ (主节点) │
└──────┬──────┘
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Secondary │ │ Secondary │ │ Arbiter │
│ (从节点) │ │ (从节点) │ │ (仲裁节点) │
└─────────────┘ └─────────────┘ └─────────────┘节点类型
| 类型 | 说明 |
|---|---|
| Primary | 主节点,处理写操作 |
| Secondary | 从节点,复制数据 |
| Arbiter | 仲裁节点,参与选举 |
| Hidden | 隐藏节点,不参与选举 |
| Delayed | 延迟节点,延迟复制 |
| Priority 0 | 优先级为0,不成为主节点 |
复制原理
Primary Secondary
│ │
├─── 写入 Oplog ───────────┤
│ │
│ 读取 Oplog
│ │
│ 应用操作
│ │部署副本集
配置文件
主节点配置
yaml
storage:
dbPath: /data/db
journal:
enabled: true
systemLog:
destination: file
path: /var/log/mongodb/mongod.log
net:
bindIp: 0.0.0.0
port: 27017
replication:
replSetName: rs0
security:
authorization: enabled
keyFile: /etc/mongodb/keyfile从节点配置
yaml
storage:
dbPath: /data/db
journal:
enabled: true
systemLog:
destination: file
path: /var/log/mongodb/mongod.log
net:
bindIp: 0.0.0.0
port: 27017
replication:
replSetName: rs0
security:
authorization: enabled
keyFile: /etc/mongodb/keyfile初始化副本集
javascript
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "mongo1:27017" },
{ _id: 1, host: "mongo2:27017" },
{ _id: 2, host: "mongo3:27017" },
],
});添加节点
javascript
rs.add("mongo4:27017");
rs.add({ host: "mongo5:27017", priority: 0, hidden: true });
rs.addArb("arbiter:27017");删除节点
javascript
rs.remove("mongo4:27017");副本集管理
查看状态
javascript
rs.status();
rs.isMaster();
rs.conf();状态字段说明
| 字段 | 说明 |
|---|---|
| name | 节点地址 |
| state | 节点状态 |
| stateStr | 状态描述 |
| health | 健康状态 |
| uptime | 运行时间 |
| optime | 最后操作时间 |
| lastHeartbeat | 最后心跳时间 |
节点状态
| 状态 | 说明 |
|---|---|
| PRIMARY | 主节点 |
| SECONDARY | 从节点 |
| STARTUP | 启动中 |
| STARTUP2 | 初始化中 |
| RECOVERING | 恢复中 |
| UNKNOWN | 未知 |
| ARBITER | 仲裁节点 |
| DOWN | 宕机 |
| ROLLBACK | 回滚中 |
| REMOVED | 已移除 |
修改配置
javascript
let cfg = rs.conf();
cfg.members[0].priority = 2;
cfg.members[1].hidden = true;
cfg.members[1].priority = 0;
rs.reconfig(cfg);强制重新配置
javascript
rs.reconfig(cfg, { force: true });选举机制
选举触发条件
- 主节点宕机
- 主节点网络中断
- 主节点主动降级
- 新节点加入
选举过程
1. 发现主节点不可用
2. 发起选举请求
3. 获取多数投票
4. 成为新的主节点优先级设置
javascript
rs.add({
host: "mongo4:27017",
priority: 2,
});
let cfg = rs.conf();
cfg.members[0].priority = 3;
rs.reconfig(cfg);禁止选举
javascript
rs.freeze(60);
rs.freeze(0);主动降级
javascript
rs.stepDown(60);
rs.stepDown({ force: true });读偏好
读偏好模式
| 模式 | 说明 |
|---|---|
| primary | 只读主节点(默认) |
| primaryPreferred | 优先主节点 |
| secondary | 只读从节点 |
| secondaryPreferred | 优先从节点 |
| nearest | 最近节点 |
设置读偏好
javascript
db.users.find().readPref("secondary");
db.users.find().readPref("secondaryPreferred");
db.users.find().readPref("nearest");连接字符串
mongodb://host1:27017,host2:27017,host3:27017/mydb?readPreference=secondary
mongodb://host1:27017,host2:27017,host3:27017/mydb?readPreference=secondaryPreferred&maxStalenessSeconds=120标签读取
javascript
let cfg = rs.conf();
cfg.members[0].tags = { dc: "east", use: "reporting" };
cfg.members[1].tags = { dc: "west", use: "reporting" };
rs.reconfig(cfg);
db.users.find().readPref("secondary", { dc: "east" });写关注
写关注级别
| 级别 | 说明 |
|---|---|
| w: 0 | 不确认 |
| w: 1 | 主节点确认(默认) |
| w: majority | 大多数节点确认 |
| w: n | n个节点确认 |
| w: 'tag' | 指定标签节点确认 |
设置写关注
javascript
db.users.insertOne(
{ name: "张三" },
{ writeConcern: { w: "majority", j: true } },
);
db.users.insertOne(
{ name: "李四" },
{ writeConcern: { w: 3, j: true, wtimeout: 5000 } },
);默认写关注
javascript
db.adminCommand({
setDefaultRWConcern: 1,
defaultWriteConcern: { w: "majority", j: true },
});特殊节点
仲裁节点
javascript
rs.addArb("arbiter:27017");仲裁节点不存储数据,只参与投票。
隐藏节点
javascript
let cfg = rs.conf();
cfg.members[2].hidden = true;
cfg.members[2].priority = 0;
rs.reconfig(cfg);隐藏节点对客户端不可见,可用于备份。
延迟节点
javascript
let cfg = rs.conf();
cfg.members[2].priority = 0;
cfg.members[2].hidden = true;
cfg.members[2].slaveDelay = 3600;
rs.reconfig(cfg);延迟节点延迟复制数据,可用于数据恢复。
优先级为0的节点
javascript
let cfg = rs.conf();
cfg.members[2].priority = 0;
rs.reconfig(cfg);优先级为0的节点永远不会成为主节点。
Oplog
Oplog 概述
Oplog(操作日志)记录所有写操作,用于复制。
查看 Oplog
javascript
use local
db.oplog.rs.find().sort({ $natural: -1 }).limit(10)Oplog 大小
javascript
db.adminCommand({ replSetResizeOplog: 1, size: 16000 });Oplog 格式
javascript
{
"ts": Timestamp(1705312200, 1),
"t": 1,
"h": 0,
"v": 2,
"op": "i",
"ns": "mydb.users",
"ui": UUID("..."),
"wall": ISODate("..."),
"o": { "_id": 1, "name": "张三" }
}Oplog 字段说明
| 字段 | 说明 |
|---|---|
| ts | 时间戳 |
| t | 选举任期 |
| op | 操作类型 |
| ns | 命名空间 |
| o | 操作文档 |
| o2 | 查询条件 |
操作类型
| 类型 | 说明 |
|---|---|
| i | 插入 |
| u | 更新 |
| d | 删除 |
| n | 空操作 |
| c | 命令 |
故障恢复
自动故障转移
当主节点不可用时,副本集会自动选举新的主节点。
手动故障转移
javascript
rs.stepDown();节点恢复
javascript
rs.add("mongo4:27017");数据同步
javascript
rs.printReplicationInfo();
rs.printSlaveReplicationInfo();重新同步
javascript
db.adminCommand({ resync: 1 });监控
副本集状态
javascript
rs.status();复制延迟
javascript
rs.printSlaveReplicationInfo();
db.adminCommand({ replSetGetStatus: 1 }).members.forEach(function (m) {
if (m.stateStr === "SECONDARY") {
print(
m.name +
": " +
(m.optimeDate - rs.status().members[0].optimeDate) / 1000 +
" seconds behind",
);
}
});监控指标
| 指标 | 说明 |
|---|---|
| replicationLag | 复制延迟 |
| oplogWindow | Oplog 时间窗口 |
| memberHealth | 节点健康状态 |
| electionCount | 选举次数 |
监控脚本
javascript
function monitorReplicaSet() {
let status = rs.status();
status.members.forEach(function (member) {
print("节点: " + member.name);
print(" 状态: " + member.stateStr);
print(" 健康: " + member.health);
if (member.optimeDate) {
print(" 最后操作: " + member.optimeDate);
}
if (member.lastHeartbeat) {
print(" 最后心跳: " + member.lastHeartbeat);
}
print("");
});
}
monitorReplicaSet();常见问题
1. 主节点选举失败
原因:
- 节点数量不足
- 网络分区
- 配置错误
解决:
javascript
rs.status();
rs.reconfig(cfg, { force: true });2. 复制延迟过大
原因:
- 从节点性能不足
- 网络延迟
- 大量写入
解决:
- 增加从节点配置
- 优化网络
- 使用批量写入
3. 数据不一致
原因:
- 网络分区
- 写关注级别过低
解决:
javascript
db.users.find().readConcern('majority')
db.users.insertOne({...}, { writeConcern: { w: 'majority' } })4. Oplog 耗尽
原因:
- 从节点宕机时间过长
- Oplog 太小
解决:
javascript
db.adminCommand({ replSetResizeOplog: 1, size: 16000 });高可用架构
三节点架构
Primary + Secondary + Secondary最简单的生产架构,可容忍一个节点故障。
五节点架构
Primary + 2 Secondary + 2 Arbiter可容忍两个节点故障。
跨机房架构
javascript
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "mongo1.dc1:27017", priority: 2, tags: { dc: "dc1" } },
{ _id: 1, host: "mongo2.dc1:27017", priority: 1, tags: { dc: "dc1" } },
{ _id: 2, host: "mongo3.dc2:27017", priority: 1, tags: { dc: "dc2" } },
{ _id: 3, host: "mongo4.dc2:27017", priority: 1, tags: { dc: "dc2" } },
{ _id: 4, host: "arbiter:27017", arbiterOnly: true },
],
});最佳实践
1. 节点数量
- 最少3个节点(1主2从)
- 奇数个投票节点
- 生产环境建议5个节点
2. 网络配置
- 使用低延迟网络
- 配置防火墙规则
- 启用 TLS 加密
3. 监控告警
- 监控节点状态
- 监控复制延迟
- 设置告警阈值
4. 备份策略
- 定期备份
- 使用隐藏节点备份
- 验证备份有效性
5. 故障演练
- 定期进行故障转移测试
- 验证自动恢复能力
- 更新运维文档