Skip to content

MongoDB - 副本集

数据复制(Replication)是指在多个服务器之间同步数据的过程,以此创建一个分布式系统。此过程通过在不同数据库服务器上维护多份数据副本,提供数据冗余并提高数据的可用性。数据复制是构建弹性应用程序、保护数据库免受单台服务器丢失以及实现硬件故障或服务中断后恢复的关键功能。

  • 数据冗余与安全性:确保数据存在多份副本,防止单节点数据丢失。
  • 高可用性:如果一台服务器发生故障,系统可以自动故障转移到另一台,确保应用程序持续在线(24/7可用性)。
  • 灾难恢复:副本集(replica set)成员可以部署在独立的物理数据中心,以抵御区域性中断。
  • 零停机维护:备份、索引重建和数据压缩等管理任务可以在从节点(secondary node)上执行,而不会影响主节点(primary node)。
  • 读扩展性:将读操作分散到从节点上,以提高应用程序的读吞吐量。
  • 应用透明性:副本集由数据库管理,驱动程序(driver)可以自动检测主节点,使得故障转移对应用程序透明无感知。

MongoDB 通过副本集(replica set)实现数据复制,副本集是一组维护相同数据集的 mongod 实例。在副本集中,一个节点被指定为主节点(primary node)。主节点接收所有写操作。所有其他实例都是从节点(secondary nodes),它们复制主节点的操作日志(oplog)并将操作应用到自己的数据集上。副本集在任何给定时间只能有一个主节点。

  • 副本集通常由三个或更多数据承载节点组成。
  • 一个节点是主节点,其余节点是从节点。
  • 所有数据写入操作都发送到主节点,然后复制到从节点。
  • 如果主节点发生故障(自动故障转移),剩余从节点之间会举行选举以选出新的主节点。
  • 当一个故障节点恢复时,它会作为从节点重新加入副本集并赶上当前数据。

下图展示了典型的副本集拓扑结构。客户端应用程序通过 MongoDB 驱动程序将所有写操作定向到主节点。主节点随后将这些更改复制到从节点。读操作可以配置为主节点(用于强一致性)或从节点(用于读扩展)。

+-----------------+
| Client |
| Application |
+--------+--------+
|
(Writes & Reads) | (Reads - Optional)
| /
+---------v----------+ +------------------+ +------------------+
| Primary Node |------>| Secondary Node |<-----| Secondary Node |
| (Accepts Writes) | | (Replicates) | | (Replicates) |
+--------------------+<----->+------------------+<----->+------------------+
(Heartbeats & Election)

虽然您可以使用命令行参数启动 mongod 实例,但现代和推荐的方法是使用配置文件。对于本地开发和测试,Docker Compose 是一个极好的工具,可以轻松地启动副本集。

  1. 创建三个数据目录,例如 data/rs1、data/rs2、data/rs3。

  2. 为每个节点创建一个配置文件(mongod.conf)。以下是第一个节点的示例:

mongod-rs1.conf
storage:
dbPath: data/rs1
net:
port: 27017
bindIp: localhost
replication:
replSetName: "rs0"
  1. 使用各自的配置文件启动每个 mongod 实例:
mongod --config mongod-rs1.conf
mongod --config mongod-rs2.conf # (使用端口 27018, dbPath data/rs2)
mongod --config mongod-rs3.conf # (使用端口 27019, dbPath data/rs3)
  1. 使用现代 MongoDB Shell (mongosh) 连接到其中一个实例:
mongosh --port 27017
  1. 在 mongosh 提示符下,初始化副本集:
// 创建一个配置对象
config = {
_id: "rs0",
members: [
{ _id: 0, host: "localhost:27017" },
{ _id: 1, host: "localhost:27018" },
{ _id: 2, host: "localhost:27019" }
]
}
// 使用配置初始化副本集
rs.initiate(config)
  1. 您可以使用 rs.status() 检查副本集的状态,并使用 rs.conf() 检查配置。

方法 2:使用 Docker Compose(推荐用于开发)

Section titled “方法 2:使用 Docker Compose(推荐用于开发)”

创建 docker-compose.yml 文件:

version: '3.8'
services:
mongo1:
image: mongo
ports:
- 27017:27017
command: ["--replSet", "rs0", "--bind_ip_all"]
mongo2:
image: mongo
command: ["--replSet", "rs0", "--bind_ip_all"]
mongo3:
image: mongo
command: ["--replSet", "rs0", "--bind_ip_all"]
mongosetup:
image: mongo
depends_on:
- mongo1
- mongo2
- mongo3
volumes:
- ./setup-rs.js:/scripts/setup-rs.js
entrypoint: [ "mongosh", "--host", "mongo1:27017", "/scripts/setup-rs.js" ]

在同一目录下创建 setup-rs.js 文件:

rs.initiate(
{
_id: "rs0",
members: [
{ _id: 0, host: "mongo1:27017" },
{ _id: 1, host: "mongo2:27017" },
{ _id: 2, host: "mongo3:27017" }
]
}
)

运行 docker-compose up 以启动并自动配置您的副本集。

要动态管理您的副本集,请连接到主节点并使用 rs.add() 和 rs.remove() 命令。

示例:添加一个运行在 localhost:27020 上的新成员:

rs.add("localhost:27020")

删除一个成员:

rs.remove("localhost:27020")

注意:您必须连接到主节点才能运行这些命令。您可以通过 db.hello().isWritablePrimary 命令进行验证,如果当前连接的是主节点,该命令将返回 true。