自建 MongoDB 实践:MongoDB 复制集
专题介绍:
八篇文章,近五万字。自建 MongoDB 实践系列文章,为您阐述日常工作中常用的 NoSQL 产品——MongoDB 运维相关的日常实战。主要涉及到:
(点击进入)
· MongoDB 文档查询 (点击进入)
· MongoDB 复制集的介绍及搭建 (本期内容)
· MongoDB 分片集群的介绍及搭建 (后续更新)
· MongoDB 的备份及恢复 (后续更新)
· MongoDB 安全加密 (后续更新)
· MongoDB Change Stream 功能介绍及代码演示 (后续更新)
· MongoDB 其他(后续更新)
本期内容:MongoDB 复制集的介绍及搭建
复制集架构概览
复制集是一组 mongod
进程一起维护相同的数据集。复制集提供了冗余及高可用。下面的几幅图是 MongoDB 复制集的工作原理:
主服务器是唯一可以随时写入的节点。从节点处于热待机状态,如果主节点发生故障,从节点可以接管主节点。一旦主节点失败,从节点将会竞选主节点角色。
我们也可以有仲裁节点。仲裁员节点不保存任何数据,其唯一目的是参与选举过程。
我们的复制集集群必须始终有奇数节点(包括仲裁节点)。三、五和七个节点是推荐的数量,所以如果初选(或更多服务器)失败,我们在选举过程中拥有多数选票。
当复制集的其他节点在超过 10 秒(可配置)内没有收到主节点的消息时,符合条件的从节点将开始进行投票选举。第一个选举并赢得多票数的节点将成为新的主节点。所有剩余的服务器现在都将从新的主节点复制,保持其作为从节点的角色,继续从新的主节点同步。
从 MongoDB 3.6 开始,如果客户端驱动程序检测到主操作已关闭,则可以一次重试写操作。复制集最多可以有 50 名节点,但其中最多只能有 7 名节点可以在选举过程中投票。
复制集是如何选举的
复制集中的所有服务器都通过心跳与所有其他节点保持定期通信。心跳是一个小包,会定期进行发送,以验证所有节点是否正常运行。
从节点与主节点通信,以从 oplog 获取最新的变更(增、删、改),并将其应用于自己的数据库中。
当主节点不可用时,所有从节点都会丢失心跳。其余节点将等到设置 electionTimeoutMillis
时间(默认时间为 10 秒)后,然后从节点将开始一轮或多轮选举,以选举产生新的主节点。
要从从节点中选择为主节点,它必须具有如下两个属性:
- 属于拥有 50%+1 选票
- 成为这个组中最新的从节点
在三个服务器各一票的简单例子中,一旦主节点不可用,其他两台服务器将各有一票(总共三分之二),那么谁拥有最新 oplog 的服务器将被选为主节点。
数据是如何复制的
- 当一个修改操作(增、删、改)到达主节点时,主节点对数据的操作将会记录下来,这些操作记录称之为
oplog
- 从节点通过在主节点上打开一个
tailable
游标不断获取新进入主节点的 oplog
,并在自己的数据上回放,以此保持跟主节点的数据一致
通过选举完成故障恢复:
- 具有投票的节点之间的两两互相发送心跳
- 当 5 次心跳未收到时,判断为节点失联
- 如果失联的主节点,从节点会发起选举,选出新的节点
- 如果失联的从节点,则不会产生新的选举
- 选举基于 RAFT一致性算法实现,选举成功的必要条件是大多数投票节点存活
- 复制集中最多可以有 50 个节点,但具有投票权的节点最多 7 个
复制集的优势
MongoDB 复制集的大部分优势,其中一些列出如下:
- 提供数据冗余,供恢复时使用
- 读扩展,客户端可以从多个从节点进行数据的读取
- 灾难恢复
- 避免停机维护
介绍了复制集的优势,那么有没其他缺憾呢?答案是肯定有的,因为完美是不存在的。
在上述优势的列表中缺少的最值得注意的是写扩展。这是因为,在 MongoDB 中,我们只能有一个主节点,只有这个主节点负责数据的写入。
当我们想扩展写入性能时,我们通常会设计和使用分片集群,这是下一章要分享的话题,敬请期待。
配置复制集
演示环境:
主机名 | IP | 角色 |
mongo01.tyun.cn | 10.20.20.19 | Primary |
mongo02.tyun.cn | 10.20.20.11 | slave1 |
mongo03.tyun.cn | 10.20.20.41 | slave2 |
准备工作
由于我们启用了认证,所以需要为每个节点使用 Keyfile(当然还有其他方式):
[root@mongo01 ~]# openssl rand -base64 756 > /etc/mongod.keyfile
[root@mongo01 ~]# cat /etc/mongod.keyfile
k+iOUxLSaYYGlclb379Ehb/AvsIGt8GaALW48IQ6QZFzT4DaFVn/T3tuiOXREgza
3EyjxqW31ReXcPhFeePRsjADw00DrEqD3iO+pHW6ZVMMQHcrIPdm4wkUKkWY7jkh
611NtKHfZKHY0QXfsrCKAn5uQXyswg6gmmssXzfsz2TEBr1R5z5n0GFz7GogOr9y
j1vYRE5NaikVLQxMSrJa2yJVGK28y/s2FJ0FB5R/z9OEeKG7LET/hqgRidztiLaD
ZEEaQU+xUPpsoUW3qF7oNlkYH4u9YBSRq8xu37GdfIgZjyQXol0TckXfFBNkn/F0
zyXRIiwtMk2ehc2kQV/n9xXMkd3vemLMTZN+aSoEPxvE4jojU5BHMS0c1Hjnpc68
D+A3NpqvXlC6u9D5XWoBJgdcVsEHgwTpOwCsk621hMDNs2/vMDnEDhjI4fat/F5x
G2ECUmL1yNQ4ls5O3BtSJwbuxqU7uXfC1wVWwAcBaeKbGOQQOvCxvx2jNIGk0zLH
XdoaXbiiGJyaIbEqstsZyYjX9rI7Br4K1KuLbKoxLhl5xJRDNFe0LAHGwXHf6YkR
9QTZwpLrbl6HbTq0MkiGsc643BfGgHgbm28ECwKe1HGABnmnrqbmo7QzFvxwHI8E
wWud2WbC/GKexSLLM50HSAU7FTbjiYjS2afIbBxVV1raKoDpbnPkRgbpXBORIP+b
1f38CAI17vGbLllaoPrB2p6bR/jR+GZhCTK4zjq1g5ssxRCbYnuH7M6x/owTIp4s
zpli6rhnpcWJGwvmpMUyeQBrrIevRZjjwSNaGACdPg52xqapx9ZMyKip0ALmOEHl
g4h4cDyiiPepbh8dz2hj/3/9RtdhyfFrMebBWImxZehVghtQR9Tp2znTBtn73TQa
ElcYK49kMn3lrMw2uqCGXBWKGQvStPR9YnRwHaQ40FjVx6YR1kv3T6kzwlKdlNZA
iMyi5u9TBCLbO+ziUKQiNX+ErJS0Qujtw0G1REBOVcqP9Wl0
密钥的长度必须在 6 到 1024 个字符之间,并且只能包含 base64 集中的字符。
设置合适的权限:
[root@mongo01 ~]# chmod 400 /etc/mongod.keyfile
[root@mongo01 ~]# chown mongod. /etc/mongod.keyfile
把 keyfile 复制到剩余的两个节点上:
[root@mongo01 ~]# scp /etc/mongod.keyfile mongo02.tyun.cn:/etc/
[root@mongo01 ~]# scp /etc/mongod.keyfile mongo03.tyun.cn:/etc/
[root@mongo01 ~]# ssh mongo02.tyun.cn -- chmod 400 /etc/mongod.keyfile
[root@mongo01 ~]# ssh mongo03.tyun.cn -- chmod 400 /etc/mongod.keyfile
[root@mongo01 ~]# ssh mongo02.tyun.cn -- chown mongod. /etc/mongod.keyfile
[root@mongo01 ~]# ssh mongo03.tyun.cn -- chown mongod. /etc/mongod.keyfile
[root@mongo01 ~]# ssh mongo02.tyun.cn -- ls -l /etc/mongod.keyfile
-r-------- 1 mongod mongod 1024 Aug 2 07:51 /etc/mongod.keyfile
[root@mongo01 ~]# ssh mongo03.tyun.cn -- ls -l /etc/mongod.keyfile
-r-------- 1 mongod mongod 1024 Aug 2 07:50 /etc/mongod.keyfile
我们使用默认的数据目录:/var/lib/mongo
。确保三台机器的配置一样,只有 bind_ip
不一样。配置分别如下(仅以第一台配置为例,其他节点的配置需要按需修改 bindIp
配置参数):
[root@mongo01 ~]# egrep -v "(^#|^$)" /etc/mongod.conf
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
storage:
dbPath: /var/lib/mongo
journal:
enabled: true
processManagement:
fork: true # fork and run in background
pidFilePath: /var/run/mongodb/mongod.pid # location of pidfile
timeZoneInfo: /usr/share/zoneinfo
net:
port: 27017
bindIp: 127.0.0.1,10.20.20.19
security:
authorization: enabled
keyFile: /etc/mongod.keyfile
replication:
replSetName: rs0
接下来分别启动三个节点上的服务:
[root@mongo01 ~]# systemctl start mongod
[root@mongo02 ~]# systemctl start mongod
[root@mongo03 ~]# systemctl start mongod
接下来配置 Primary、slave 及 arbiter 节点。客户端连接到 Primary:
[root@mongo01 ~]# mongo --authenticationDatabase "admin" -u "root" -p
MongoDB shell version v4.4.15
Enter password: xxxxxxxx # 在此输入密码
> rs.initiate({
_id: "rs0",
members: [{
_id: 0,
host: "mongo01.tyun.cn:27017"
},{
_id: 1,
host: "mongo02.tyun.cn:27017"
},{
_id: 2,
host: "mongo03.tyun.cn:27017"
}]
})
{ "ok" : 1 }
# 注意观察提示符已经发生了变化
rs0:SECONDARY>
rs0:SECONDARY>
......
rs0:SECONDARY>
rs0:SECONDARY>
rs0:PRIMARY>
配置之前的提示符是 ">
",进行了复制集的初始化后,提示变成了 rs0:SECONDARY
,这个时候所有的节点都是从节点角色,因为要进行一轮或多轮的投票选举。过了一段时间,有一个节点(mongo01)经过投票选举被选为了主节点,所以它的提示符变成了 rs0:PRIMARY
。
查看复制集的状态信息:
rs0:PRIMARY> rs.status()
{
"set" : "rs0",
"date" : ISODate("2022-08-02T08:04:34.878Z"),
"myState" : 1,
"term" : NumberLong(1),
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"majorityVoteCount" : 2,
"writeMajorityCount" : 2,
"votingMembersCount" : 3,
"writableVotingMembersCount" : 3,
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1659427469, 1),
"t" : NumberLong(1)
},
"lastCommittedWallTime" : ISODate("2022-08-02T08:04:29.498Z"),
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1659427469, 1),
"t" : NumberLong(1)
},
"readConcernMajorityWallTime" : ISODate("2022-08-02T08:04:29.498Z"),
"appliedOpTime" : {
"ts" : Timestamp(1659427469, 1),
"t" : NumberLong(1)
},
"durableOpTime" : {
"ts" : Timestamp(1659427469, 1),
"t" : NumberLong(1)
},
"lastAppliedWallTime" : ISODate("2022-08-02T08:04:29.498Z"),
"lastDurableWallTime" : ISODate("2022-08-02T08:04:29.498Z")
},
"lastStableRecoveryTimestamp" : Timestamp(1659427439, 1),
"electionCandidateMetrics" : {
"lastElectionReason" : "electionTimeout",
"lastElectionDate" : ISODate("2022-08-02T07:59:59.469Z"),
"electionTerm" : NumberLong(1),
"lastCommittedOpTimeAtElection" : {
"ts" : Timestamp(0, 0),
"t" : NumberLong(-1)
},
"lastSeenOpTimeAtElection" : {
"ts" : Timestamp(1659427188, 1),
"t" : NumberLong(-1)
},
"numVotesNeeded" : 2,
"priorityAtElection" : 1,
"electionTimeoutMillis" : NumberLong(10000),
"numCatchUpOps" : NumberLong(0),
"newTermStartDate" : ISODate("2022-08-02T07:59:59.486Z"),
"wMajorityWriteAvailabilityDate" : ISODate("2022-08-02T08:00:00.622Z")
},
"members" : [
{
"_id" : 0,
"name" : "mongo01.tyun.cn:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 1701,
"optime" : {
"ts" : Timestamp(1659427469, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2022-08-02T08:04:29Z"),
"lastAppliedWallTime" : ISODate("2022-08-02T08:04:29.498Z"),
"lastDurableWallTime" : ISODate("2022-08-02T08:04:29.498Z"),
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1659427199, 1),
"electionDate" : ISODate("2022-08-02T07:59:59Z"),
"configVersion" : 1,
"configTerm" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 1,
"name" : "mongo02.tyun.cn:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 286,
"optime" : {
"ts" : Timestamp(1659427469, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1659427469, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2022-08-02T08:04:29Z"),
"optimeDurableDate" : ISODate("2022-08-02T08:04:29Z"),
"lastAppliedWallTime" : ISODate("2022-08-02T08:04:29.498Z"),
"lastDurableWallTime" : ISODate("2022-08-02T08:04:29.498Z"),
"lastHeartbeat" : ISODate("2022-08-02T08:04:33.506Z"),
"lastHeartbeatRecv" : ISODate("2022-08-02T08:04:33.006Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncSourceHost" : "mongo01.tyun.cn:27017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1,
"configTerm" : 1
},
{
"_id" : 2,
"name" : "mongo03.tyun.cn:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 286,
"optime" : {
"ts" : Timestamp(1659427469, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1659427469, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2022-08-02T08:04:29Z"),
"optimeDurableDate" : ISODate("2022-08-02T08:04:29Z"),
"lastAppliedWallTime" : ISODate("2022-08-02T08:04:29.498Z"),
"lastDurableWallTime" : ISODate("2022-08-02T08:04:29.498Z"),
"lastHeartbeat" : ISODate("2022-08-02T08:04:33.507Z"),
"lastHeartbeatRecv" : ISODate("2022-08-02T08:04:33.006Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncSourceHost" : "mongo01.tyun.cn:27017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1,
"configTerm" : 1
}
],
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1659427469, 1),
"signature" : {
"hash" : BinData(0,"MUcQYY28dUYGQB+XmqfVO7kUMVU="),
"keyId" : NumberLong("7127185549797883908")
}
},
"operationTime" : Timestamp(1659427469, 1)
}
我们看一下 members
信息:
rs0:PRIMARY> var members = rs.status().members
rs0:PRIMARY> for (i = 0; i < members.length; i++) {
if (members[i].stateStr === "PRIMARY") { continue }
print("Secondary:", members[i].name, "SyncSource:", members[i].syncSourceHost)
}
Secondary: mongo02.tyun.cn:27017 SyncSource: mongo01.tyun.cn:27017
Secondary: mongo03.tyun.cn:27017 SyncSource: mongo01.tyun.cn:27017
通过输出我们可以看到两个从节点 mongo02.tyun.cn:27017
及 mongo03.tyun.cn:27017
分别从主节点 mongo01.tyun.cn:27017
上进行数据的同步。
至此,我们的复制集已经设置完成。虽然复制集已经创建完成,但是我们的工作才刚刚开始。
读首选项
默认情况下,所有写入和读取都来自主节点。从节点复制数据,但不用于查询。MongoDB 官方驱动程序支持五个级别的数据读首选项:
读首选项的模式 | 描述 |
primary | 这是默认的模式,从主节点读 |
primaryPreferred | 在这种模式下,应用也是从主节点读数据,但是当主节点不可用时,会从从节点读取数据 |
secondary | 完全从从节点读取数据 |
secondaryPreferred | 优先从从节点读取数据。当从节点不可用时,从主节点读取数据 |
nearest | 应用程序将从节点中就近读取数据,而不考虑节点的类型 |
通过上述提供的选项,可以很方便地配置读写分离、就近读取等策略,细分控制读取策略。上述的这些选项该怎么用呢?
- 链接字符串的形式:
mongodb://db0.example.com,db1.example.com,db2.example.com/?replicaSet=myRepl&readPreference=secondary
- Mongo shell 的形式:
db.collection.find().readPref("secondary")
连接复制集
复制集搭建完成后,该怎么连接使用呢?
连接复制集与连接单个节点是没有什么区别的。
配置完成就可以做数据同步的测试了,在 Primary 上创建数据,然后查看数据,然后登录其他从节点查看数据。操作如下:
[root@mongo01 ~]# mongo --authenticationDatabase "admin" -u "tyun" -p
MongoDB shell version v4.4.15
Enter password: xxxxxx # 在此输入密码
connecting to: mongodb://127.0.0.1:27017/?authSource=admin&compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("d325d424-b594-4e5d-903f-407e185ad3f6") }
MongoDB server version: 4.4.15
rs0:PRIMARY> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
order 0.000GB
test 0.000GB
rs0:PRIMARY> use test
switched to db test
rs0:PRIMARY> show tables
customer
inventory
oscars
我们再次创建一个新的测试表:
rs0:PRIMARY> db.books.insert({name: "云迁移专家", price: 100})
WriteResult({ "nInserted" : 1 })
这时登录到从节点:
[root@mongo01 ~]# mongo --host 10.20.20.11:27017 --authenticationDatabase "admin" -u "tyun" -p
MongoDB shell version v4.4.15
Enter password: xxxxx # 在此输入密码
connecting to: mongodb://10.20.20.11:27017/?authSource=admin&compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("f958dbce-8de4-41b7-a037-22565ff0f914") }
MongoDB server version: 4.4.15
rs0:SECONDARY>
rs0:SECONDARY> show dbs
uncaught exception: Error: listDatabases failed:{
"topologyVersion" : {
"processId" : ObjectId("62e8d7f431935e1af2ac1825"),
"counter" : NumberLong(4)
},
"operationTime" : Timestamp(1659428959, 1),
"ok" : 0,
"errmsg" : "not master and slaveOk=false",
"code" : 13435,
"codeName" : "NotPrimaryNoSecondaryOk",
"$clusterTime" : {
"clusterTime" : Timestamp(1659428959, 1),
"signature" : {
"hash" : BinData(0,"9x5C9QDI8H8S8dvEekvyCn7DS10="),
"keyId" : NumberLong("7127185549797883908")
}
}
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:147:19
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:99:12
shellHelper.show@src/mongo/shell/utils.js:937:13
shellHelper@src/mongo/shell/utils.js:819:15
@(shellhelp2):1:1
当我们执行命令的时候,报错了。主要是因为设置了从节点不能进行读操作,只需简单设置下即可:
rs0:SECONDARY> rs.secondaryOk() # 这是设置命令,之前的命令是 rs.slaveOk()
rs0:SECONDARY> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
order 0.000GB
test 0.000GB
# 查看 books 表中的数据
rs0:SECONDARY> db.books.find()
{ "_id" : ObjectId("62e8e024229630850e8feb36"), "name" : "云迁移专家", "price" : 100 }
rs0:SECONDARY> db.books.find().pretty()
{
"_id" : ObjectId("62e8e024229630850e8feb36"),
"name" : "云迁移专家",
"price" : 100
}
再次插入一条数据:
rs0:PRIMARY> db.books.insert({name: "云迁移最佳实践", price: 999})
WriteResult({ "nInserted" : 1 })
# 从节点进行查看
rs0:SECONDARY> db.books.find().pretty()
{
"_id" : ObjectId("62e8e024229630850e8feb36"),
"name" : "云迁移专家",
"price" : 100
}
{
"_id" : ObjectId("62e8e1aa229630850e8feb37"),
"name" : "云迁移最佳实践",
"price" : 999
}
复制集的日常操作
复制集的管理可能比单服务器部署所需的要复杂得多。在本节中,我们将重点关注复制集中一些最常见的管理及维护性的操作。
节点优先级设置
MongoDB 允许我们为每个节点设置不同的优先级,设置如下:
var conf = rs.conf()
// 将 0 号节点的优先级调整为 10
conf.members[0].priority = 10;
// 将 1 号节点调整为 hidden 节点
conf.members[1].hidden = true;
// hidden 节点必须配置{priority: 0}
conf.members[1].priority = 0;
// 应用以上调整
rs.reconfig(conf);
当要对复制集中的节点进行维护时,优先级的设置就派上用场了。
要在设置集群后更改优先级,我们必须使用 mongo shell 连接到主服务器并获取配置对象:
> cfg = rs.conf()
然后,我们可以将节点优先级属性更改为我们想要的值:
> cfg.members[0].priority = 0.778
> cfg.members[1].priority = 999.9999
每个节点的默认优先级为1。数值是浮点精度,优先级可以从 0(永远不会成为主)到 1000。
当主节点宕机并再次发生选举时,这些高优先级的节点将会进行选举,也是最有可能赢得选举的节点。
设置 0 优先级的复制集节点
在某些情况下(例如,如果我们有多个数据中心),我们希望一些节点永远无法成为主服务器。
在具有多个数据中心复制的场景中,我们的主数据中心(位于英国)可能有一个主节点和一个从节点,以及一台节点位于俄罗斯。在这种情况下,我们不希望我们位于俄罗斯的服务器成为主节点,因为它会给位于英国的应用程序服务器产生延迟。在这种情况下,我们将设置位于俄罗斯的服务器的优先级为 0。
> var conf = rs.conf()
// 将 0 号节点的优先级调整为 10
> conf.members[0].priority = 10;
// 应用以上调整
> rs.reconfig(conf);
确保每个节点都运行相同版本的 MongoDB,否则可能会出现意外行为。避免在业务高峰期重新配置复制集集群。重新配置复制集可能会迫使选举新的主节点,这将关闭所有活跃的连接,并可能导致 10-30 秒的停机时间。尝试确定最低流量时间窗口,以运行重新配置等维护操作,并始终有一个恢复计划,以防出现问题。
隐藏复制集节点
隐藏的复制集节点通常用于特殊任务。客户端看不到它们,不会显示在 db.isMaster()
mongo shell 命令和类似的管理命令中,并且出于所有目的,客户端不会考虑(即读取首选项)。
隐藏节点可以投票支持选举,但永远不会成为主服务器。隐藏的复制集节点只会同步到主服务器,不会被客户端读取数据。因此,它具有与主服务器相同的写入负载(用于复制目的),但本身没有读取负载。
设置隐藏节点的操作如下:
> cfg = rs.conf()
> cfg.members[0].priority = 0
> cfg.members[0].hidden = true
> rs.reconfig(cfg)
延迟复制集节点
在大多数情况下,我们希望有一个节点在较早的时间点保存我们数据的副本。这有助于从大量人为错误中恢复过来,例如手滑或不在状态而误删数据。
一个延迟复制节点必须要设置的两个配置为:priority = 0
及 hidden = true
。延迟复制节点可以参加选举,但是对客户端不可见并且不会被选举为主节点。设置如下:
> cfg = rs.conf()
> cfg.members[0].priority = 0
> cfg.members[0].hidden = true
> cfg.members[0].slaveDelay = 7200
> rs.reconfig(cfg)
改变 oplog 大小
// 查看 oplog 大小
rs0:PRIMARY> rs.printReplicationInfo()
configured oplog size: 2482.624755859375MB
log length start to end: 7486261secs (2079.52hrs)
oplog first event time: Sat Aug 27 2022 14:38:52 GMT+0000 (UTC)
oplog last event time: Tue Nov 22 2022 06:09:53 GMT+0000 (UTC)
now: Tue Nov 22 2022 06:09:59 GMT+0000 (UTC)
rs0:PRIMARY> use local
rs0:PRIMARY> db.oplog.rs.stats(1024*1024).maxSize
2482
// 改变大小为 16G
rs0:PRIMARY> db.adminCommand({replSetResizeOplog: 1, size: 16000}
链式复制
MongoDB 中的复制通常发生在主从节点之间。在某些情况下,我们可能想从另一个从节点复制,而不是从主节点复制。链式复制有助于减轻主节点读负载。设置如下:
> cfg.settings.chainingAllowed = true
重新配置复制集
当我们有大多数节点不可用并且我们仍然有足够的服务器来启动复制集时,我们可以强制仅对已存在的节点进行重新配置。这是一种临时解决方案和最后的手段。
首先,我们获取复制集的配置:
shard0:PRIMARY> cfg = rs.conf()
shard0:PRIMARY> cfg.members
[
{
"_id" : 0,
"host" : "mongo01.tyun.cn:27010",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 1,
"host" : "mongo02.tyun.cn:27010",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 2,
"host" : "mongo03.tyun.cn:27010",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 1
}
]
我们可以使用 printjson(cfg)
打印上面的配置信息。假设现存的集群中还剩余节点 1、2、3 的话:
shard0:PRIMARY> cfg.members = [cfg.members[1] , cfg.members[2] , cfg.members[3]]
shard0:PRIMARY> rs.reconfig(cfg, {force : true})
通过使用 force: true
选项,我们使其重新配置。当然,我们的复制集中至少需要三名幸存的节点才能发挥作用。
重要的是尽快删除故障服务器,通过
kill
进程或将其从网络中剔除,以避免意外结果;这些服务器可能认为它们仍然是集群的一部分,但集群却不再承认它们。
总结
关于复制集就介绍到这里,我们在此进行一个小结。建议一开始就使用复制集,因为我们不知道数据的增长如何、及未来会发生什么,算是未雨绸缪吧。建议一个集群至少要有三个节点,而且集群的节点的数量必须为奇数。复制集不仅仅只是数据的同步,我们还可以在从节点上执行读操作,来分摊主节点的压力。
最后生产部署的一些考量:
- 需要在单独的物理主机上部署每个 mongod 实例。如果我们使用的是虚拟机,请确保它们被分配到不同的物理主机上。使用
bind_ip
选项来确保服务器映射到特定的网络接口和端口地址。 - 使用防火墙阻止对任何其他端口的访问和/或仅允许在应用程序服务器和 MongoDB 服务器之间访问。
- 启用认证机制
下一节我们将介绍 MongoDB 的分片集群。分片集群要比复制集复杂一些、所需的维护成本及费用都要高些。敬请期待,谢谢大家。
文章转载自公众号:新钛云服