延迟节点介绍
延迟节点反应的是数据库在较早时刻的状态,比如一个配置了延迟时间为3600秒的延迟节点,那么这个延迟节点的数据就是primary一个小时之前的状态。延迟节点存在的意义主要是解决应用层面数据错误的问题,假设管理员对数据库做了一个误操作,如果数据是时时传输的话,那么这个误操作立刻就会被备库同步,这样就导致了备库数据的损坏。而延迟节点就可以解决这样的问题,因为这个误操作需要等待一个延迟时间才能同步到备库。
延迟节点的要求:
- 必须是优先级为0,因为延迟节点的延迟属性,很显然它不具备成为主库的条件。
- 必须配置掩藏属性,因为要始终防止应用程序查看和查询延迟成员。
- 配置投票属性为0可以提高数据库性能
PSD架构中的隐患
在一个PSD(Primary-Secondary-Delay)的架构中,存一个问题,就是这个Delay节点的votes配置到底是应该配置为0还是1的问题。先看看文档中的说明:
在文档中提到,需要确保Delay的成员配置为无投票权也就是votes为0的情况,这个很容易理解,因为在Secondary宕机的情况下主节点的写入需要等待延迟节点的写入,如果延迟节点配置了10分钟的延迟,那么就需要等待10min才能确认写请求(majority)。但是,如果将Delay的votes配置为非零,那么又会导致另外一个问题,看上去的3节点实际上只有2个节点具有投票权,那么在主库或者备库任何节点宕机的时候,都会导致集群无法选主的情况,那么就造成了集群不可写入的情况。
验证
试验拓扑如下
角色 | 端口 | DBPath |
Primary | 27017 | /mongodb/data/rs/db1 |
Secondary | 27018 | /mongodb/data/rs/db2 |
Delay | 27019 | /mongodb/data/rs/db3 |
创建副本集
a) 使用如下参数启动3个mongodb实例
[root@vm002 ~]# cat /etc/mongod_1.conf
storage:
dbPath: "/mongodb/data/rs/db1"
systemLog:
destination: file
path: "/mongodb/log/rs/dblog1/mongo1.log"
logAppend: true
net:
port: "27017"
bindIp: localhost,vm002
processManagement:
fork: true
security:
keyFile: /mongodb/pki/myrp-keyfile
replication:
replSetName: rs0
[root@vm002 ~]# cat /etc/mongod_2.conf
略 <---修改路径即可
[root@vm002 ~]# cat /etc/mongod_2.conf
略 <---修改路径即可
b) 修改db3为延迟节点
cfg=rs.conf()
db3的编号为_id=2
{
"_id" : 2,
"host" : "vm002:27019",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"secondaryDelaySecs" : NumberLong(0),
"votes" : 1
}
--根据文档中的要求,延迟节点的优先级为0,同时配置掩藏
cfg.members[2].priority=0
cfg.members[2].hidden=true
cfg.members[2].secondaryDelaySecs=20
rs.reconf(cfg)
--当前配置如下
当前配置如下:
{
"_id" : 0,
"host" : "vm002:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"secondaryDelaySecs" : NumberLong(0),
"votes" : 1
},
{
"_id" : 1,
"host" : "vm002:27018",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"secondaryDelaySecs" : NumberLong(0),
"votes" : 1
},
{
"_id" : 2,
"host" : "vm002:27019",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : true, <---掩藏
"priority" : 0, <---优先级为0
"tags" : {
},
"secondaryDelaySecs" : NumberLong(20), <---20秒延迟
"votes" : 1
}
]
测试验证1
a) 确认延迟节点生效
主库执行insert操作,预期db2能立刻同步,db3延迟30秒同步
rs0:PRIMARY> db.abc.insert(
... { a:'test' },
... { writeConcern: { w: "majority", wtimeout: 15000 } }
... )
WriteResult({ "nInserted" : 1 })
--db2立刻能查看
rs0:SECONDARY> rs.slaveOk()
rs0:SECONDARY> db.abc.find()
{ "_id" : ObjectId("62b12b904f96ba620b6307e9"), "a" : 1 }
--db3延迟20秒能查看
rs0:SECONDARY> rs.slaveOk()
rs0:SECONDARY> db.abc.find()
{ "_id" : ObjectId("62b12b904f96ba620b6307e9"), "a" : 1 }
b) 停止db2
mongod --shutdown -f /etc/mongod_2.conf
rs0:PRIMARY> rs.status()
{
"set" : "rs0",
"date" : ISODate("2022-06-21T03:33:47.213Z"),
"myState" : 1,
"term" : NumberLong(2),
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"majorityVoteCount" : 2,
"writeMajorityCount" : 2, <---因为3个节点都有votes,所以majority依然为2
"votingMembersCount" : 3,
"writableVotingMembersCount" : 3,
...
}
c) 再次验证写入
rs0:PRIMARY> db.abc.insert( { a:'test' }, { writeConcern: { w: "majority", wtimeout: 15000 } } )
WriteResult({
"nInserted" : 1,
"writeConcernError" : {
"code" : 64,
"codeName" : "WriteConcernFailed",
"errmsg" : "waiting for replication timed out",
"errInfo" : {
"wtimeout" : true,
"writeConcern" : {
"w" : "majority",
"wtimeout" : 15000,
"provenance" : "clientSupplied"
}
}
}
})
此时就发现主库的写入会被hang住,因为majority写入需要获取2票,因为延迟节点的特性,需要等待20秒之后才能返回写入的确认信息,这个时候就会造成主库写入的等待。需要注意的是,虽然收到了timeout的错误,对主库的写入依然是成功的,报错信息只是提示等待复制同步的超时。这里就证明了延迟节点配置为非0的确会有延迟问题
测试验证2
调整延迟节点votes为0
rs0:PRIMARY> rs.conf()
...
{
"_id" : 2,
"host" : "vm002:27019",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : true,
"priority" : 0,
"tags" : {
},
"secondaryDelaySecs" : NumberLong(20),
"votes" : 0 <---当前votes为0
}
...
在延迟节点votes属性为0的情况下,我们直接关闭Secondary。
这个时候就会发现,剩下的两个成员都是Secondary,集群中无法选Primary了。
结论以及处理方式
在PSD的架构中的确不能解决高可用的问题,如果要解决高可用问题,个人觉得可以在增加一个仲裁节点,这也是在官方文档中描述三节点副本集的时候只提供了PSA和PSS架构的原因。