MongoDB副本集

副本集(Replica Set) 是MongoDB中用于提高数据可用性和容错能力的一种机制

副本集是一个包含多个MongoDB实例的集群,有一个主节点,和一个或多个从节点,主节点负责处理所有写入操作,从节点定期复制主节点数据保持数据一致性

副本集类型和角色

两种类型

主节点(Primary)类型:数据操作的主要连接点,可读写。

从节点(Secondary)类型:数据冗余备份节点,可以读或选举。
三种角色

主要成员(Primary):接收所有写操作,就是主节点。

副本成员(Replicate):从主节点通过复制操作以维护相同的数据集,即备份数据,不可写操作,可以读操作

仲裁者(Arbiter):不保留任何数据的副本,只具有投票选举作用

副本集和传统主从集群对比

特性 副本集 (Replica Set) 主从复制 (Master-Slave)
架构本质 自动化高可用集群 手动配置的数据复制
故障转移 自动选举:主节点故障时,从节点自动投票选举新主节点 手动切换:需要管理员干预将从节点提升为主节点
节点管理 动态角色:所有节点对等,角色由系统自动分配 静态配置:主从角色在启动时固定
数据一致性 强一致性保障:支持写关注,防止数据回滚 弱一致性:无内置机制确保写操作安全
客户端连接 智能路由:客户端连接副本集名称,驱动自动发现主节点 硬编码:应用需要直接指定主节点地址
MongoDB官方状态 推荐标准(当前版本唯一支持的高可用方案) 已废弃(不再维护)

副本集本质上是主从复制的自动化升级版本。它保留了主从的数据复制机制,但增加了选举、心跳检测、自动故障恢复等分布式系统功能。

对于现代应用需求,副本集在可用性、可靠性和运维效率上全面优于传统主从复制,因此 MongoDB 已用副本集完全取代主从复制模式。

副本集创建

1. Docker Compose一键启动

使用 Docker Compose 一键启动一个包含3个节点的副本集,并进行初始化。

yaml 复制代码
version: '3.8'

services:
  mongo1:
    image: mongo:latest
    container_name: mongo1
    restart: always
    ports:
      - "27017:27017" # 将主节点映射到本地端口,方便连接
    command: mongod --replSet myReplicaSet --bind_ip_all
    networks:
      - mongo-network

  mongo2:
    image: mongo:latest
    container_name: mongo2
    restart: always
    ports:
      - "27018:27017" # 第二个节点映射到不同端口
    command: mongod --replSet myReplicaSet --bind_ip_all
    networks:
      - mongo-network

  mongo3:
    image: mongo:latest
    container_name: mongo3
    restart: always
    ports:
      - "27019:27017" # 第三个节点映射到不同端口
    command: mongod --replSet myReplicaSet --bind_ip_all
    networks:
      - mongo-network

networks:
  mongo-network:
    driver: bridge

2. 启动副本集容器

bash 复制代码
docker-compose up -d


3. 初始化副本集

连接到任一容器实例

bash 复制代码
docker exec -it mongo1 mongosh

执行初始化命令

bash 复制代码
rs.initiate(
  {
    _id: "myReplicaSet",
    members: [
      { _id: 0, host: "mongo1:27017" },
      { _id: 1, host: "mongo2:27017" },
      { _id: 2, host: "mongo3:27017" }
    ]
  }
)

这里用的 host 是 容器名:端口,因为它们在 Docker 自定义网络内可以通过容器名互相发现。

查看副本集状态

bash 复制代码
rs.status()

members 数组下的每个成员的 stateStr 字段,包含一个 "PRIMARY",两个 "SECONDARY"。

bash 复制代码
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.主从复制验证

在主节点上插入数据

bash 复制代码
use testdb
db.testcoll.insertOne({ name: "Hello Replica Set!", value: 1 })

在从节点上读取数据,连接从节点,设置允许从从节点读后查询数据

bash 复制代码
docker exec -it mongo2 mongosh
db.getMongo().setReadPref('secondaryPreferred')
use testdb
db.testcoll.find()


得见刚才在主节点插入的数据。这说明数据已经自动从 Primary 复制到了 Secondary。

相关推荐
小陈工1 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
科技小花6 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸6 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain6 小时前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希7 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神7 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员7 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java7 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿7 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb
不知名的老吴7 小时前
Redis的延迟瓶颈:TCP栈开销无法避免
数据库·redis·缓存