MongoDB-2 副本集
MongoDB 副本集(Replica Set)详解
-
副本集是 MongoDB 提供的数据冗余和高可用性解决方案,由一组维护相同数据集的 mongod 实例组成。副本集包含多个数据承载节点和一个可选的仲裁节点。
-
副本集的优势
高可用性:自动故障转移,减少停机时间
数据安全:多副本存储,防止单点故障
读写分离:分散读取负载
维护友好:允许无停机维护
灾难恢复:延迟节点可用于恢复误操作 -
最佳实践
副本集至少应有3个数据承载成员
将成员分布在不同的物理位置(数据中心)
使用奇数个投票成员(或添加仲裁节点)
监控复制延迟(rs.printSlaveReplicationInfo)
定期测试故障转移过程 -
注意事项
副本集需要至少3个成员才能实现自动故障转移
写操作需要"大多数"成员确认(w: "majority")
网络分区可能导致脑裂情况
重新配置副本集可能导致短暂的选举过程
副本集的组成
一个典型的 MongoDB 副本集包含以下成员:
-
主节点(Primary):
- 接收所有写操作
- 记录操作日志(oplog)
- 一个副本集只能有一个主节点
-
从节点(Secondary):
- 复制主节点的数据
- 可以配置为只读或特殊用途(如报表节点)
- 可以成为新的主节点(故障转移时)
-
仲裁节点(Arbiter)(可选):
- 不存储数据
- 仅参与选举投票
- 用于解决选举中的平票情况
副本集的工作原理
-
数据同步机制
副本集通过 oplog(操作日志) 实现数据同步:
主节点记录所有改变数据集的操作到 oplog
从节点异步复制并应用这些操作
每个成员都会维护自己的 oplog 副本 -
自动故障转移
当主节点不可用时(超过 electionTimeoutMillis,默认10秒):
剩余成员发起选举
获得大多数投票的从节点成为新主节点
客户端驱动会自动检测并路由写操作到新主节点 -
读写一致性
写操作:默认只在主节点执行
读操作:可配置从从节点读取(最终一致性)
实现副本集
在三台机器上分别安装mongodb
mongo01 192.168.40.31:27017
mongo02 192.168.40.32:27017
mongo03 192.168.40.33:27017
修改mongodb配置文件
以mongo01为例子,其他两个节点只改IP
net:
port: 27017
bindIp: 192.168.40.31
replication:
replSetName: rs1 # 集群名称,三台机器设置一致
sharding:
clusterRole: shardsvr # 集群角色名称,分片
完整配置
# mongod.conf
# for documentation of all options, see:
# http://docs.mongodb.org/manual/reference/configuration-options/
# Where and how to store data.
storage:
dbPath: /var/lib/mongodb
# engine:
# wiredTiger:
# where to write logging data.
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
# network interfaces
net:
port: 27017
bindIp: 192.168.40.31
# how the process runs
processManagement:
timeZoneInfo: /usr/share/zoneinfo
#security:
#operationProfiling:
replication:
replSetName: rs1
sharding:
clusterRole: shardsvr
## Enterprise-Only Options:
#auditLog:
启动三台mongod服务
systemctl start mongod
连接到一个节点
mongosh --host mongo01:27017
使用默认参数初始化副本集的一个节点(会成为主节点)
rs.initiate()
添加从节点
rs.add("mongo02:27017")
添加仲裁节点
rs.addArb("mongo03:27017")
管理副本集
查看副本集信息
rs.conf() // 查看副本集配置信息
rs.status() // 查看副本集状态信息
查看副本集配置信息
rs1 [direct: primary] test> rs.conf()
{
_id: 'rs1',
version: 6,
term: 5,
members: [
{
_id: 0,
host: '192.168.40.31:27017',
arbiterOnly: false,
buildIndexes: true,
hidden: false,
priority: 1,
tags: {},
secondaryDelaySecs: Long('0'),
votes: 1
},
{
_id: 1,
host: 'mongo02:27017',
arbiterOnly: false,
buildIndexes: true,
hidden: false,
priority: 1,
tags: {},
secondaryDelaySecs: Long('0'),
votes: 1
},
{
_id: 2,
host: 'mongo03:27017',
arbiterOnly: true,
buildIndexes: true,
hidden: false,
priority: 0,
tags: {},
secondaryDelaySecs: Long('0'),
votes: 1
}
],
protocolVersion: Long('1'),
writeConcernMajorityJournalDefault: true,
settings: {
chainingAllowed: true,
heartbeatIntervalMillis: 2000,
heartbeatTimeoutSecs: 10,
electionTimeoutMillis: 10000,
catchUpTimeoutMillis: -1,
catchUpTakeoverDelayMillis: 30000,
getLastErrorModes: {},
getLastErrorDefaults: { w: 1, wtimeout: 0 },
replicaSetId: ObjectId('696851b2f99f992953d87345')
}
}
查看副本集状态信息
rs1 [direct: primary] test> rs.status()
{
set: 'rs1',
date: ISODate('2026-01-16T10:16:42.484Z'),
myState: 1,
term: Long('5'),
syncSourceHost: '',
syncSourceId: -1,
heartbeatIntervalMillis: Long('2000'),
majorityVoteCount: 2,
writeMajorityCount: 2,
votingMembersCount: 3,
writableVotingMembersCount: 2,
optimes: {
lastCommittedOpTime: { ts: Timestamp({ t: 1768558597, i: 1 }), t: Long('5') },
lastCommittedWallTime: ISODate('2026-01-16T10:16:37.310Z'),
readConcernMajorityOpTime: { ts: Timestamp({ t: 1768558597, i: 1 }), t: Long('5') },
appliedOpTime: { ts: Timestamp({ t: 1768558597, i: 1 }), t: Long('5') },
durableOpTime: { ts: Timestamp({ t: 1768558597, i: 1 }), t: Long('5') },
lastAppliedWallTime: ISODate('2026-01-16T10:16:37.310Z'),
lastDurableWallTime: ISODate('2026-01-16T10:16:37.310Z')
},
lastStableRecoveryTimestamp: Timestamp({ t: 1768558577, i: 1 }),
electionCandidateMetrics: {
lastElectionReason: 'stepUpRequestSkipDryRun',
lastElectionDate: ISODate('2026-01-16T10:14:57.271Z'),
electionTerm: Long('5'),
lastCommittedOpTimeAtElection: { ts: Timestamp({ t: 1768558494, i: 1 }), t: Long('4') },
lastSeenOpTimeAtElection: { ts: Timestamp({ t: 1768558494, i: 1 }), t: Long('4') },
numVotesNeeded: 2,
priorityAtElection: 1,
electionTimeoutMillis: Long('10000'),
priorPrimaryMemberId: 1,
numCatchUpOps: Long('0'),
newTermStartDate: ISODate('2026-01-16T10:14:57.294Z'),
wMajorityWriteAvailabilityDate: ISODate('2026-01-16T10:14:57.315Z')
},
electionParticipantMetrics: {
votedForCandidate: true,
electionTerm: Long('4'),
lastVoteDate: ISODate('2026-01-16T07:32:13.690Z'),
electionCandidateMemberId: 1,
voteReason: '',
lastAppliedOpTimeAtElection: { ts: Timestamp({ t: 1768548438, i: 1 }), t: Long('3') },
maxAppliedOpTimeInSet: { ts: Timestamp({ t: 1768548438, i: 1 }), t: Long('3') },
priorityAtElection: 1
},
members: [
{
_id: 0,
name: '192.168.40.31:27017',
health: 1,
state: 1,
stateStr: 'PRIMARY',
uptime: 114629,
optime: { ts: Timestamp({ t: 1768558597, i: 1 }), t: Long('5') },
optimeDate: ISODate('2026-01-16T10:16:37.000Z'),
lastAppliedWallTime: ISODate('2026-01-16T10:16:37.310Z'),
lastDurableWallTime: ISODate('2026-01-16T10:16:37.310Z'),
syncSourceHost: '',
syncSourceId: -1,
infoMessage: '',
electionTime: Timestamp({ t: 1768558497, i: 1 }),
electionDate: ISODate('2026-01-16T10:14:57.000Z'),
configVersion: 6,
configTerm: 5,
self: true,
lastHeartbeatMessage: ''
},
{
_id: 1,
name: 'mongo02:27017',
health: 1,
state: 2,
stateStr: 'SECONDARY',
uptime: 15744,
optime: { ts: Timestamp({ t: 1768558597, i: 1 }), t: Long('5') },
optimeDurable: { ts: Timestamp({ t: 1768558597, i: 1 }), t: Long('5') },
optimeDate: ISODate('2026-01-16T10:16:37.000Z'),
optimeDurableDate: ISODate('2026-01-16T10:16:37.000Z'),
lastAppliedWallTime: ISODate('2026-01-16T10:16:37.310Z'),
lastDurableWallTime: ISODate('2026-01-16T10:16:37.310Z'),
lastHeartbeat: ISODate('2026-01-16T10:16:42.129Z'),
lastHeartbeatRecv: ISODate('2026-01-16T10:16:42.094Z'),
pingMs: Long('0'),
lastHeartbeatMessage: '',
syncSourceHost: '192.168.40.31:27017',
syncSourceId: 0,
infoMessage: '',
configVersion: 6,
configTerm: 5
},
{
_id: 2,
name: 'mongo03:27017',
health: 1,
state: 7,
stateStr: 'ARBITER',
uptime: 72,
lastHeartbeat: ISODate('2026-01-16T10:16:42.130Z'),
lastHeartbeatRecv: ISODate('2026-01-16T10:16:42.127Z'),
pingMs: Long('0'),
lastHeartbeatMessage: '',
syncSourceHost: '',
syncSourceId: -1,
infoMessage: '',
configVersion: 6,
configTerm: 5
}
],
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1768558597, i: 1 }),
signature: {
hash: Binary.createFromBase64('AAAAAAAAAAAAAAAAAAAAAAAAAAA=', 0),
keyId: Long('0')
}
},
operationTime: Timestamp({ t: 1768558597, i: 1 })
}
初始化副本集
// 使用默认参数初始化副本集
rs.initiate()
//使用配置描述初始化副本集
rs.initiate(
{
_id: "rs1",
members: [
{ _id: 0, host : "mongo01:27017" },
{ _id: 1, host : "mongo02:27017" },
{ _id: 2, host : "mongo03:27017" }
]
}
)
添加节点
# 添加从节点,并指定特定的属性
rs.add(
{
_id: <int>,
host: <string>, #主机名称(必须)
arbiterOnly: <boolean>, #是否为仲裁节点
buildIndexes: <boolean>, #是否要创建索引
hidden: <boolean>, #是否隐藏
priority: <number>, #优先级
tags: <document>, #设置读偏好的标签
secondaryDelaySecs: <int>, #比主节点延迟多少秒
votes: <number> #是否参与投票,有效值是0或1
},
arbiterOnly: <boolean> #是否为仲裁节点
)
# 添加仲裁节点
rs.addArb("mongo03:27017")
示例:
//添加从节点,并指定特定的属性
rs.add( { host: "mongo02:27017", priority: 0 } )
删除节点
rs.remove("mongo03:27017")
选举相关
// 让主节点降级,默认60秒内不参与选举,需要在主节点执行
rs.stepDown()
// 指定“不可参选”时长(单位:秒),比如120秒
rs.stepDown(120)
// 强制降级(忽略次要节点的同步状态,仅应急使用)
rs.stepDown(60, { force: true })
// 在指定时间内不参与主节点选举(单位:秒),比如60秒,可在任意副本集节点(主节点/从节点/仲裁节点)执行
rs.freeze(60)
// 取消节点的选举冻结状态
rs.freeze(0)
// 在冻结期内再次执行,更新冻结时长(比如延长到120秒)
rs.freeze(120)
// 使从节点从指定的节点(主或从节点)同步数据
rs.syncFrom("mongo03:27017")
// 查看节点的同步源是哪个节点
rs.printSecondaryReplicationInfo()
副本集的高级配置
1. 成员优先级(priority)
cfg = rs.conf()
cfg.members[0].priority = 2
cfg.members[1].priority = 1
cfg.members[2].priority = 0.5
rs.reconfig(cfg)
2. 隐藏节点(hidden)
cfg = rs.conf()
cfg.members[2].hidden = true
cfg.members[2].priority = 0
rs.reconfig(cfg)
3. 延迟节点(delayed)
cfg = rs.conf()
cfg.members[2].priority = 0
cfg.members[2].hidden = true
cfg.members[2].slaveDelay = 3600 // 延迟1小时
rs.reconfig(cfg)

浙公网安备 33010602011771号