MongoDB副本集
副本集(Replica Set) 是MongoDB中用于提高数据可用性和容错能力的一种机制
副本集是一个包含多个MongoDB实例的集群,有一个主节点,和一个或多个从节点,主节点负责处理所有写入操作,从节点定期复制主节点数据保持数据一致性
副本集类型和角色
两种类型
主节点(Primary)类型:数据操作的主要连接点,可读写。
从节点(Secondary)类型:数据冗余备份节点,可以读或选举。
三种角色
主要成员(Primary):接收所有写操作,就是主节点。
副本成员(Replicate):从主节点通过复制操作以维护相同的数据集,即备份数据,不可写操作,可以读操作
仲裁者(Arbiter):不保留任何数据的副本,只具有投票选举作用
副本集和传统主从集群对比
特性 | 副本集 (Replica Set) | 主从复制 (Master-Slave) |
---|---|---|
架构本质 | 自动化高可用集群 | 手动配置的数据复制 |
故障转移 | 自动选举:主节点故障时,从节点自动投票选举新主节点 | 手动切换:需要管理员干预将从节点提升为主节点 |
节点管理 | 动态角色:所有节点对等,角色由系统自动分配 | 静态配置:主从角色在启动时固定 |
数据一致性 | 强一致性保障:支持写关注,防止数据回滚 | 弱一致性:无内置机制确保写操作安全 |
客户端连接 | 智能路由:客户端连接副本集名称,驱动自动发现主节点 | 硬编码:应用需要直接指定主节点地址 |
MongoDB官方状态 | 推荐标准(当前版本唯一支持的高可用方案) | 已废弃(不再维护) |
副本集本质上是主从复制的自动化升级版本。它保留了主从的数据复制机制,但增加了选举、心跳检测、自动故障恢复等分布式系统功能。
对于现代应用需求,副本集在可用性、可靠性和运维效率上全面优于传统主从复制,因此 MongoDB 已用副本集完全取代主从复制模式。
副本集创建
1. Docker Compose一键启动
使用 Docker Compose 一键启动一个包含3个节点的副本集,并进行初始化。
version: '3.8'services:mongo1:image: mongo:latestcontainer_name: mongo1restart: alwaysports:- "27017:27017" # 将主节点映射到本地端口,方便连接command: mongod --replSet myReplicaSet --bind_ip_allnetworks:- mongo-networkmongo2:image: mongo:latestcontainer_name: mongo2restart: alwaysports:- "27018:27017" # 第二个节点映射到不同端口command: mongod --replSet myReplicaSet --bind_ip_allnetworks:- mongo-networkmongo3:image: mongo:latestcontainer_name: mongo3restart: alwaysports:- "27019:27017" # 第三个节点映射到不同端口command: mongod --replSet myReplicaSet --bind_ip_allnetworks:- mongo-networknetworks:mongo-network:driver: bridge
2. 启动副本集容器
docker-compose up -d
3. 初始化副本集
连接到任一容器实例
docker exec -it mongo1 mongosh
执行初始化命令
rs.initiate({_id: "myReplicaSet",members: [{ _id: 0, host: "mongo1:27017" },{ _id: 1, host: "mongo2:27017" },{ _id: 2, host: "mongo3:27017" }]}
)
这里用的 host 是 容器名:端口,因为它们在 Docker 自定义网络内可以通过容器名互相发现。
查看副本集状态
rs.status()
members 数组下的每个成员的 stateStr 字段,包含一个 “PRIMARY”,两个 “SECONDARY”。
members: [{_id: 0,name: 'mongo1:27017',health: 1,state: 1,stateStr: 'PRIMARY',uptime: 135,optime: { ts: Timestamp({ t: 1758793236, i: 1 }), t: Long('1') },optimeDate: ISODate('2025-09-25T09:40:36.000Z'),optimeWritten: { ts: Timestamp({ t: 1758793236, i: 1 }), t: Long('1') },optimeWrittenDate: ISODate('2025-09-25T09:40:36.000Z'),lastAppliedWallTime: ISODate('2025-09-25T09:40:36.349Z'),lastDurableWallTime: ISODate('2025-09-25T09:40:36.349Z'),lastWrittenWallTime: ISODate('2025-09-25T09:40:36.349Z'),syncSourceHost: '',syncSourceId: -1,infoMessage: 'Could not find member to sync from',electionTime: Timestamp({ t: 1758793206, i: 1 }),electionDate: ISODate('2025-09-25T09:40:06.000Z'),configVersion: 1,configTerm: 1,self: true,lastHeartbeatMessage: ''},{_id: 1,name: 'mongo2:27017',health: 1,state: 2,stateStr: 'SECONDARY',uptime: 44,optime: { ts: Timestamp({ t: 1758793236, i: 1 }), t: Long('1') },optimeDurable: { ts: Timestamp({ t: 1758793236, i: 1 }), t: Long('1') },optimeWritten: { ts: Timestamp({ t: 1758793236, i: 1 }), t: Long('1') },optimeDate: ISODate('2025-09-25T09:40:36.000Z'),optimeDurableDate: ISODate('2025-09-25T09:40:36.000Z'),optimeWrittenDate: ISODate('2025-09-25T09:40:36.000Z'),lastAppliedWallTime: ISODate('2025-09-25T09:40:36.349Z'),lastDurableWallTime: ISODate('2025-09-25T09:40:36.349Z'),lastWrittenWallTime: ISODate('2025-09-25T09:40:36.349Z'),lastHeartbeat: ISODate('2025-09-25T09:40:38.279Z'),lastHeartbeatRecv: ISODate('2025-09-25T09:40:39.283Z'),pingMs: Long('0'),lastHeartbeatMessage: '',syncSourceHost: 'mongo1:27017',syncSourceId: 0,infoMessage: '',configVersion: 1,configTerm: 1},{_id: 2,name: 'mongo3:27017',health: 1,state: 2,stateStr: 'SECONDARY',uptime: 44,optime: { ts: Timestamp({ t: 1758793236, i: 1 }), t: Long('1') },optimeDurable: { ts: Timestamp({ t: 1758793236, i: 1 }), t: Long('1') },optimeWritten: { ts: Timestamp({ t: 1758793236, i: 1 }), t: Long('1') },optimeDate: ISODate('2025-09-25T09:40:36.000Z'),optimeDurableDate: ISODate('2025-09-25T09:40:36.000Z'),optimeWrittenDate: ISODate('2025-09-25T09:40:36.000Z'),lastAppliedWallTime: ISODate('2025-09-25T09:40:36.349Z'),lastDurableWallTime: ISODate('2025-09-25T09:40:36.349Z'),lastWrittenWallTime: ISODate('2025-09-25T09:40:36.349Z'),lastHeartbeat: ISODate('2025-09-25T09:40:38.277Z'),lastHeartbeatRecv: ISODate('2025-09-25T09:40:39.281Z'),pingMs: Long('0'),lastHeartbeatMessage: '',syncSourceHost: 'mongo1:27017',syncSourceId: 0,infoMessage: '',configVersion: 1,configTerm: 1}],
4.主从复制验证
在主节点上插入数据
use testdb
db.testcoll.insertOne({ name: "Hello Replica Set!", value: 1 })
在从节点上读取数据,连接从节点,设置允许从从节点读后查询数据
docker exec -it mongo2 mongosh
db.getMongo().setReadPref('secondaryPreferred')
use testdb
db.testcoll.find()
得见刚才在主节点插入的数据。这说明数据已经自动从 Primary 复制到了 Secondary。