MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

MongoDB复制集配置与管理

2022-10-295.0k 阅读

一、MongoDB 复制集简介

MongoDB 复制集是由一组 MongoDB 实例组成的集群,其中包含一个主节点(Primary)和多个从节点(Secondary)。复制集的主要目的是提供数据冗余、高可用性以及灾难恢复能力。

在复制集中,主节点负责处理所有的写操作,当主节点接收到写请求时,它会将这些操作记录到 oplog(操作日志)中。从节点会定期从主节点同步 oplog,并应用这些操作来保持与主节点的数据一致性。如果主节点出现故障,复制集中的其他节点会通过选举机制选出一个新的主节点,以确保服务的连续性。

二、复制集配置前的准备工作

  1. 环境准备 确保每个 MongoDB 实例所在的服务器之间网络连通。如果是在本地测试,可以使用不同的端口来模拟多个实例。例如,准备三个 MongoDB 实例,分别监听 27017、27018 和 27019 端口。

  2. 安装 MongoDB 根据操作系统的不同,按照官方文档的指引安装 MongoDB。以 Ubuntu 为例,可以使用以下命令安装:

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.0.list
sudo apt-get update
sudo apt-get install -y mongodb-org
  1. 配置文件准备 为每个 MongoDB 实例创建独立的配置文件。假设我们有三个实例,分别命名为 mongodb1.confmongodb2.confmongodb3.conf

mongodb1.conf 示例:

systemLog:
  destination: file
  path: /var/log/mongodb/mongodb1.log
  logAppend: true
storage:
  dbPath: /var/lib/mongodb1
  journal:
    enabled: true
net:
  port: 27017
  bindIp: 127.0.0.1
replication:
  oplogSizeMB: 1024
  replSetName: myReplSet

mongodb2.conf 示例:

systemLog:
  destination: file
  path: /var/log/mongodb/mongodb2.log
  logAppend: true
storage:
  dbPath: /var/lib/mongodb2
  journal:
    enabled: true
net:
  port: 27018
  bindIp: 127.0.0.1
replication:
  oplogSizeMB: 1024
  replSetName: myReplSet

mongodb3.conf 示例:

systemLog:
  destination: file
  path: /var/log/mongodb/mongodb3.log
  logAppend: true
storage:
  dbPath: /var/lib/mongodb3
  journal:
    enabled: true
net:
  port: 27019
  bindIp: 127.0.0.1
replication:
  oplogSizeMB: 1024
  replSetName: myReplSet

在上述配置文件中,systemLog 部分定义了日志相关的设置,storage 部分指定了数据库文件的存储路径和日志设置,net 部分设置了监听端口和绑定的 IP 地址,replication 部分设置了 oplog 的大小以及复制集的名称。

三、启动 MongoDB 实例

根据前面准备的配置文件,启动三个 MongoDB 实例。

启动第一个实例:

mongod -f /etc/mongod1.conf

启动第二个实例:

mongod -f /etc/mongod2.conf

启动第三个实例:

mongod -f /etc/mongod3.conf

四、初始化复制集

  1. 连接到其中一个实例 使用 mongo 命令连接到其中一个 MongoDB 实例,例如连接到监听 27017 端口的实例:
mongo --port 27017
  1. 初始化复制集 在 MongoDB shell 中,执行以下命令初始化复制集:
rs.initiate({
  _id: "myReplSet",
  members: [
    { _id: 0, host: "127.0.0.1:27017" },
    { _id: 1, host: "127.0.0.1:27018" },
    { _id: 2, host: "127.0.0.1:27019" }
  ]
})

在上述命令中,_id 是复制集的名称,必须与配置文件中的 replSetName 一致。members 数组定义了复制集中的成员,每个成员通过 _idhost 来指定。

执行上述命令后,如果初始化成功,会看到类似如下的输出:

{
  "ok" : 1,
  "operationTime" : Timestamp(1634232330, 1),
  "$clusterTime" : {
    "clusterTime" : Timestamp(1634232330, 1),
    "signature" : {
      "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
      "keyId" : NumberLong(0)
    }
  }
}

五、查看复制集状态

在 MongoDB shell 中,可以使用 rs.status() 命令查看复制集的状态。例如:

rs.status()

输出结果会包含复制集的详细信息,包括主节点、从节点的状态,成员的健康状况等。以下是一个简化的示例输出:

{
  "set" : "myReplSet",
  "date" : ISODate("2021-10-14T15:25:14.420Z"),
  "myState" : 1,
  "term" : NumberLong(1),
  "syncingTo" : "",
  "syncSourceHost" : "",
  "syncSourceId" : -1,
  "heartbeatIntervalMillis" : NumberLong(2000),
  "majorityVoteCount" : 2,
  "writeMajorityCount" : 2,
  "votingMembersCount" : 3,
  "writableVotingMembersCount" : 3,
  "members" : [
    {
      "_id" : 0,
      "name" : "127.0.0.1:27017",
      "health" : 1,
      "state" : 1,
      "stateStr" : "PRIMARY",
      "uptime" : 1234,
      "optime" : {
        "ts" : Timestamp(1634232330, 1),
        "t" : NumberLong(1)
      },
      "optimeDate" : ISODate("2021-10-14T15:25:30Z"),
      "lastHeartbeat" : ISODate("2021-10-14T15:25:13.233Z"),
      "lastHeartbeatRecv" : ISODate("2021-10-14T15:25:13.407Z"),
      "pingMs" : NumberLong(0),
      "syncingTo" : "",
      "syncSourceHost" : "",
      "syncSourceId" : -1,
      "infoMessage" : "",
      "electionTime" : Timestamp(1634232328, 1),
      "electionDate" : ISODate("2021-10-14T15:25:28Z"),
      "configVersion" : 1
    },
    {
      "_id" : 1,
      "name" : "127.0.0.1:27018",
      "health" : 1,
      "state" : 2,
      "stateStr" : "SECONDARY",
      "uptime" : 1234,
      "optime" : {
        "ts" : Timestamp(1634232330, 1),
        "t" : NumberLong(1)
      },
      "optimeDate" : ISODate("2021-10-14T15:25:30Z"),
      "lastHeartbeat" : ISODate("2021-10-14T15:25:13.234Z"),
      "lastHeartbeatRecv" : ISODate("2021-10-14T15:25:13.408Z"),
      "pingMs" : NumberLong(0),
      "syncingTo" : "127.0.0.1:27017",
      "syncSourceHost" : "127.0.0.1:27017",
      "syncSourceId" : 0,
      "infoMessage" : "",
      "configVersion" : 1
    },
    {
      "_id" : 2,
      "name" : "127.0.0.1:27019",
      "health" : 1,
      "state" : 2,
      "stateStr" : "SECONDARY",
      "uptime" : 1234,
      "optime" : {
        "ts" : Timestamp(1634232330, 1),
        "t" : NumberLong(1)
      },
      "optimeDate" : ISODate("2021-10-14T15:25:30Z"),
      "lastHeartbeat" : ISODate("2021-10-14T15:25:13.234Z"),
      "lastHeartbeatRecv" : ISODate("2021-10-14T15:25:13.408Z"),
      "pingMs" : NumberLong(0),
      "syncingTo" : "127.0.0.1:27017",
      "syncSourceHost" : "127.0.0.1:27017",
      "syncSourceId" : 0,
      "infoMessage" : "",
      "configVersion" : 1
    }
  ],
  "ok" : 1,
  "operationTime" : Timestamp(1634232330, 1),
  "$clusterTime" : {
    "clusterTime" : Timestamp(1634232330, 1),
    "signature" : {
      "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
      "keyId" : NumberLong(0)
    }
  }
}

在这个输出中,myState 字段表示当前连接的节点在复制集中的状态,1 表示主节点,2 表示从节点。members 数组中每个元素代表一个成员,stateStr 字段明确了每个成员是 PRIMARY 还是 SECONDARY

六、复制集写操作原理

  1. 主节点写操作 当一个写操作到达主节点时,主节点首先会将操作记录到 oplog 中。oplog 是一个特殊的、固定大小的集合,它记录了所有对数据库的写操作。主节点会在 oplog 中添加一条新的记录,包含操作的详细信息,如插入、更新或删除的文档内容,以及操作的时间戳等。

例如,执行一个插入操作:

use mydb
db.users.insertOne({name: "John", age: 30})

主节点会在 oplog 中记录类似如下的操作:

{
  "ts" : Timestamp(1634232500, 1),
  "t" : NumberLong(1),
  "op" : "i",
  "ns" : "mydb.users",
  "o" : {
    "_id" : ObjectId("616666666666666666666666"),
    "name" : "John",
    "age" : 30
  }
}

在这个 oplog 记录中,ts 是时间戳,t 是事务编号,op 表示操作类型(i 代表插入),ns 是命名空间(数据库名.集合名),o 是实际的操作对象。

  1. 从节点同步操作 从节点会定期轮询主节点的 oplog,获取新的操作记录。从节点通过 syncSourceHostsyncSourceId 来确定从哪个主节点同步。当从节点获取到新的 oplog 记录后,会按照顺序应用这些操作到自己的数据集上,以保持与主节点的数据一致性。

例如,上述插入操作记录被从节点获取后,从节点会在本地执行相同的插入操作:

use mydb
db.users.insertOne({_id: ObjectId("616666666666666666666666"), name: "John", age: 30})

七、复制集读操作原理

  1. 主节点读操作 默认情况下,读操作会被路由到主节点。当客户端发起读请求时,主节点直接从自己的数据集返回数据。例如:
use mydb
db.users.find()

主节点会在本地的 mydb.users 集合中查询数据并返回给客户端。

  1. 从节点读操作 为了分担主节点的读压力,可以配置从节点接受读请求。在 MongoDB shell 中,可以通过以下命令配置从节点可读:
rs.slaveOk()

之后,客户端可以通过连接从节点来执行读操作。从节点会从自己同步的数据集中返回数据。例如,连接到 27018 端口的从节点并执行读操作:

mongo --port 27018
use mydb
db.users.find()

需要注意的是,由于从节点同步存在一定的延迟,从从节点读取的数据可能不是最新的。在对数据一致性要求较高的场景下,建议仍然从主节点读取数据。

八、添加和移除复制集成员

  1. 添加成员 假设要添加一个新的 MongoDB 实例,监听端口为 27020,先按照前面的步骤准备好配置文件 mongodb4.conf 并启动该实例。

然后连接到主节点,在 MongoDB shell 中执行以下命令添加成员:

rs.add("127.0.0.1:27020")
  1. 移除成员 如果要移除一个成员,例如移除 127.0.0.1:27019 这个节点,连接到主节点,在 MongoDB shell 中执行:
rs.remove("127.0.0.1:27019")

九、故障转移与选举机制

  1. 主节点故障检测 复制集中的成员通过心跳机制来检测其他成员的健康状况。每个成员会定期向其他成员发送心跳消息,如果在一定时间内没有收到某个成员的心跳响应,就会认为该成员出现故障。

  2. 选举机制 当主节点出现故障时,复制集中的从节点会发起选举,以选出一个新的主节点。选举过程遵循一定的规则:

  • 具有最新 oplog 的节点更有可能被选为新的主节点。
  • 节点的优先级(可以通过配置文件中的 priority 字段设置)也会影响选举结果,优先级高的节点更容易被选为新的主节点。

例如,在配置文件中设置某个节点的优先级为 2:

replication:
  oplogSizeMB: 1024
  replSetName: myReplSet
  priority: 2

在选举过程中,节点之间会通过投票来确定新的主节点。只有当一个节点获得大多数成员的投票(超过一半的投票)时,才能成为新的主节点。

十、配置优先级与仲裁节点

  1. 优先级配置 如前面提到的,可以在配置文件中为每个成员设置优先级。优先级范围是 0 到 1000,默认值为 1。优先级高的节点在选举时更有优势成为主节点。

例如,将 mongodb2.conf 中的节点优先级设置为 5:

replication:
  oplogSizeMB: 1024
  replSetName: myReplSet
  priority: 5
  1. 仲裁节点 仲裁节点是一种特殊的复制集成员,它不存储数据,只参与选举过程。仲裁节点的主要作用是在选举时提供额外的投票,帮助确定新的主节点。

要添加一个仲裁节点,先启动一个新的 MongoDB 实例,例如监听 27021 端口,配置文件 mongodb-arbiter.conf 示例如下:

systemLog:
  destination: file
  path: /var/log/mongodb/mongodb-arbiter.log
  logAppend: true
net:
  port: 27021
  bindIp: 127.0.0.1
replication:
  replSetName: myReplSet
  arbiterOnly: true

然后连接到主节点,在 MongoDB shell 中执行以下命令添加仲裁节点:

rs.addArb("127.0.0.1:27021")

仲裁节点的存在可以在复制集成员数量为偶数时,避免选举出现平局的情况,确保复制集能够快速选出新的主节点,提高高可用性。

十一、复制集备份与恢复

  1. 备份 可以使用 mongodump 命令对复制集进行备份。连接到主节点,执行以下命令:
mongodump --uri="mongodb://127.0.0.1:27017" --out=/backup/path

上述命令会将主节点上的所有数据库和集合备份到指定的 /backup/path 目录下。

  1. 恢复 使用 mongorestore 命令进行恢复。假设备份文件在 /backup/path 目录下,连接到要恢复的 MongoDB 实例(可以是主节点或从节点),执行:
mongorestore --uri="mongodb://127.0.0.1:27017" /backup/path

执行恢复操作时,确保目标实例的数据库和集合不存在或者为空,以免数据冲突。

十二、监控与性能优化

  1. 监控工具
  • mongostat:可以实时监控 MongoDB 实例的状态,包括插入、查询、更新、删除操作的频率,内存使用情况,连接数等。例如:
mongostat --host 127.0.0.1 --port 27017
  • mongotop:用于分析 MongoDB 实例的读写操作分布,显示每个集合的读写时间占比。例如:
mongotop --host 127.0.0.1 --port 27017
  1. 性能优化
  • 索引优化:合理创建索引可以显著提高查询性能。通过分析查询语句,为经常用于查询条件的字段创建索引。例如,对于 db.users.find({age: 30}) 这样的查询,可以为 age 字段创建索引:
use mydb
db.users.createIndex({age: 1})
  • ** oplog 大小调整**:根据实际的写操作频率和数据量,合理调整 oplog 的大小。如果 oplog 太小,可能导致从节点同步不及时;如果太大,会占用过多的磁盘空间。可以在配置文件中通过 oplogSizeMB 字段进行调整。

  • 读写分离优化:根据业务需求,合理配置读操作到从节点,减轻主节点的压力。同时,要注意从节点同步延迟对读数据一致性的影响。

通过以上对 MongoDB 复制集的配置、管理、原理以及相关操作的介绍,希望能帮助读者深入理解和应用 MongoDB 复制集,构建高可用、可靠的数据库系统。在实际应用中,还需要根据具体的业务场景和需求,进一步优化和调整复制集的配置和性能。