TOC
MongoDB副本集应用
对于副本集的复制关键两个要点是操作日志(oplog)和heartbeat,要想能真正完成副本集的副本功能并且要想能够在分裂时能够让从节点提升为主节点,我们通常至少应该有三个节点,哪怕其中一个节点我们用来做仲裁节点,由此接下来尝试使用三个节点来部署一个所谓的副本集;
主机名 |
网络地址 |
作用 |
node01 |
172.16.1.1 |
MongoDB |
node02 |
172.16.1.2 |
MongoDB |
node03 |
172.16.1.3 |
MongoDB |
配置参数
replSetName:设定副本集集群的名称;
使用命令
rs.status():获取副本集当前的状态信息;
rs.initiate():初始化当前副本集;
rs.initiate(cfg):以指定配置对当前的副本集做初始化;
rs.conf():查看当前副本集配置信息;
_id: 当前的节点ID;
host : 当前的节点名称;
arbiterOnly : 当前节点是否为仲裁节点;
buildIndexes : 主要用来设置是否这个节点的数据用于查询;
hidden : 是否为一个隐藏节点;
priority : 是否为主节点
tags : 当前成员的表示,可以添加标识来说明这个节点的用处等;
slaveDelay : 是否是一个延迟复制节点;
votes : 是否拥有选票;
getLastErrorDefaults:是否获取最新的错误信息;
heartbeatTimeoutSecs:获取传递心跳信息的时间;
rs.reconfig(cfg):重新修改当前副本集的配置;
rs.add(hostportstr):以IP和端口的方式向当前副本集添加主机;
rs.add(membercfgobj):以配置的方式向当前副本集添加主机;
rs.isMaster():查看自己是否在master;
部署
利用三个节点部署一个MongoDB的副本集;
# 在各个节点上分别安装MongoDB
[root@node01 ~]# cat > /etc/yum.repos.d/mongodb.repo << EOF
[mongodb]
name=MongoDB Repository
baseurl=https://mirrors.tuna.tsinghua.edu.cn/mongodb/yum/el7/
gpgcheck=0
enabled=1
EOF
# 安装MongoDB
[root@node01 ~]# yum install -y mongodb-org
[root@node01 ~]# systemctl start mongod.service
# 在node01上修改配置,指定副本集名称
[root@node01 ~]# grep '^[^#]' /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: 0.0.0.0 # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.
replication:
replSetName: TestSet # 指定副本集名称
# 第一个节点启动会创建相关日志,并且第一个节点默认为主节点[root@node01 ~]# mongo
> use local
switched to db local
> show collections
replset.election
replset.minvalid
replset.oplogTruncateAfterPoint
startup_log
system.rollback.id
# 启动第二和第三个节点
[root@node02 ~]# systemctl start mongod.service
[root@node03 ~]# systemctl start mongod.service
# 初始化副本集
[root@node01 ~]# mongo
> rs.initiate()
{
"info2" : "no configuration specified. Using a default configuration for the set",
"me" : "node01.cce.com:27017",
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1590132858, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1590132858, 1)
}
# 在node01将node02加入副本集
TestSet:PRIMARY> rs.add("node02.cce.com:27017")
# 在node01将node03加入副本集
TestSet:PRIMARY> rs.add("node03.cce.com:27017")
# 在主节点创建测试数据
[root@node01 ~]# mongo
TestSet:PRIMARY> use cce
switched to db cce
TestSet:PRIMARY> for (i=1;i<=100000;i++) db.students.insert({name:"student"+i,age:i%120,address:"#89 Wenhua Road,Zhengzhou,China"})
# 默认情况下,从阶段是不允许查询的NotMasterNoSlaveOk,需要执行rs.slaveOk()才可以查询
[root@node02 ~]# mongo
TestSet:SECONDARY> rs.slaveOk()
TestSet:SECONDARY> show dbs;
admin 0.000GB
cce 0.001GB # 可以看到,我们在主节点插入的数据,已经同步到的从节点
config 0.000GB
local 0.002GB
# 测试在主节点写入数据
TestSet:PRIMARY> db.classes.insert({Class: "One", members: 30})
# 在从从节点查询试一下
TestSet:SECONDARY> db.classes.find()
{ "_id" : ObjectId("5ec78451c56d42a38d649ad4"), "Class" : "One", "members" : 30 }
测试宕机
如果重试我们将主节点宕机了,那么10秒钟之后就会发起选举,其中一个节点就有有可能会成为一个新的主节点;
# 测试,将主节点强制转为从节点
TestSet:PRIMARY> rs.stepDown()
{
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1590135378, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1590135378, 1)
}
TestSet:SECONDARY> # 可以看到身份已经转为了从节点
TestSet:SECONDARY> rs.isMaster()
{
"hosts" : [
"node01.cce.com:27017",
"node02.cce.com:27017",
"node03.cce.com:27017"
],
"setName" : "TestSet",
"setVersion" : 3,
"ismaster" : false,
"secondary" : true,
"primary" : "node02.cce.com:27017", # 此时node02已经接管了主节点
"me" : "node01.cce.com:27017",
...
# 查看oplog的同步时间信息
TestSet:SECONDARY> db.printReplicationInfo()
configured oplog size: 4804.21640586853MB # 当前oplog的大小
log length start to end: 2718secs (0.76hrs) # 当前oplog保存的数据距离now时间范围
oplog first event time: Fri May 22 2020 15:34:18 GMT+0800 (CST) # 第一次oplog时间的时间
oplog last event time: Fri May 22 2020 16:19:36 GMT+0800 (CST) # 最后一次oplog时间的时间
now: Fri May 22 2020 16:19:37 GMT+0800 (CST) # 当前时间
副本集选举机制
说白了,就是触发选举的事件;
第一,新副本集初始化时;
第二,从节点联系不到主节点时;
第三,主节点下台时,就像上面是stepDown,或者某从节点有更高的优先级,且已经满足成为主节点的其他任何条件,那么这个时候这个从节点会触发一次新的选举,或者说,主节点无法联系到副本集的多数方,它会自动下台;
副本集重新选举的影响条件
对于主节点的选举影响条件有几个;
第一是信条信息,如果信条信息收不到了,那就会重新选举;
第二个是优先级,每一个节点都有自己选举优先级,默认都为1,如果大家都一样,其他条件将作为考虑条件,而0优先级的节点是不可能成为主节点的,并且也不能触发选举过程,如果一个节点发现自己的优先级特别的高,它是可以强行触发整个集群重新发起选举的让自己上台的;
第三个是optime也是考虑要素,optime是指某成员要素,最近一次应用于本地oplog条目的时间戳,每一个成员节点,它的时间戳必须跟主节点最后一次更新的时间保持一致,如果某一个从节点的optime与其他成员节点相比,它的时间要落后,那么很显然它就不可能成为主节点;
第四个,网络连接,表示一旦发生网络分区之后,拥有票数多的一方将会可以继续成为主节点的一方;
测试优先级
现在我们的主节点是node02,现在测试一下,将node03的优先级调整一下,使其拥有更大的几率获得主节点,需要在主节点执行,因为从节点是不具备数据操作权利的;
# 在主节点上将配置先保存于一个变量
[root@node02 ~]# mongo
TestSet:SECONDARY> cfg=rs.conf()
# 在主节点上设定node03的优先级,因为默认为1,我们将node03设定为2
TestSet:SECONDARY> cfg.members[2].priority=2
# 在主节点上重新配置我们的副本集
TestSet:PRIMARY> rs.reconfig(cfg)
{
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1590137054, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1590137054, 1)
}
TestSet:PRIMARY>
TestSet:PRIMARY>
TestSet:PRIMARY>
TestSet:PRIMARY>
TestSet:PRIMARY>
TestSet:PRIMARY>
TestSet:PRIMARY>
TestSet:PRIMARY>
TestSet:PRIMARY>
TestSet:PRIMARY>
TestSet:PRIMARY>
TestSet:SECONDARY>
TestSet:SECONDARY>
TestSet:SECONDARY>
TestSet:SECONDARY>
# 可以看到,一段时间后,它就自动转为从节点了
TestSet:SECONDARY> rs.isMaster()
{
"hosts" : [
"node01.cce.com:27017",
"node02.cce.com:27017",
"node03.cce.com:27017"
],
"setName" : "TestSet",
"setVersion" : 4,
"ismaster" : false,
"secondary" : true,
"primary" : "node03.cce.com:27017", # 此时已经成功将我们的node03设定为了主节点
...
仲裁节点
继续上面的架构,我们将node01设置为主节点,并且新增一个node04节点为仲裁节点;
# 在主节点上将配置先保存于一个变量
[root@node03 ~]# mongo
TestSet:SECONDARY> cfg=rs.conf()
# 在主节点上设定node03的优先级,因为默认为1,我们将node03设定为2
TestSet:SECONDARY> cfg.members[2].priority=2
# 在主节点上重新配置我们的副本集
TestSet:PRIMARY> rs.reconfig(cfg)
# 在node04上安装MongoDB
[root@node04 ~]# cat > /etc/yum.repos.d/mongodb.repo << EOF
[mongodb]
name=MongoDB Repository
baseurl=https://mirrors.tuna.tsinghua.edu.cn/mongodb/yum/el7/
gpgcheck=0
enabled=1
EOF
# 安装MongoDB
[root@node04 ~]# yum install -y mongodb-org
[root@node04 ~]# systemctl start mongod.service
# 配置node04加入副本集
[root@node01 ~]# grep '^[^#]' /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: 0.0.0.0 # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.
replication:
replSetName: TestSet # 指定副本集名称
# 在主节点上将node04加入集群,并且设定为仲裁节点,当然,我们也可以通过cfg的方式来配置,此处就直接add了TestSet:PRIMARY> rs.addArb("node04.cce.com:27017")
# 在主节点查看副本集状态
TestSet:PRIMARY> rs.status()
{
"...
{
"_id" : 3,
"name" : "node04.cce.com:27017",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER",
"uptime" : 45,
"lastHeartbeat" : ISODate("2020-05-22T08:58:54.764Z"),
"lastHeartbeatRecv" : ISODate("2020-05-22T08:58:54.812Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"configVersion" : 8
}
...
查看从节点是否落后
查看从节点的数据同步是否落后于主节点;
TestSet:PRIMARY> rs.printSlaveReplicationInfo()
source: node02.cce.com:27017
syncedTo: Fri May 22 2020 16:59:47 GMT+0800 (CST)
0 secs (0 hrs) behind the primary # node02落后主节点0秒
source: node03.cce.com:27017
syncedTo: Fri May 22 2020 16:59:47 GMT+0800 (CST)
0 secs (0 hrs) behind the primary # node03落后主节点0秒