【背景】
最近线上一套集群出现一个服务器故障导致闪存卡异常,经过抢救后无效,只能更换新闪存卡,导致这个节点上数据丢失,只能进行初始化来恢复.执行初始化动作很简单,启动空实例即可.全量复制很顺利,但通过oplog将从库恢复到一致性时,发现速度非常慢.由此案例了解逻辑复制大概过程、存在问题以及其他初始化方式.
【初始化方式】
1、使用逻辑初始化方式
2、使用基于文件复制初始化方式
【那些场景会触发初始化】
1、备库应用OPLOG超出源端OPLOG时间窗口
2、备库由于各种原因导致无法启动或者进入回滚状态
3、迁移备库到新服务器
4、清理实例碎片
5、新增备实例
备注:针对以上场景可以选择逻辑初始化或者基于文件复制,使用姿势不当会导致MajorityCount改变从而影响性能(谨慎)
【逻辑初始化复制过程】
1、逻辑初始化流程图【3.4以及之后版本】
2、从T1时开始应用OPLOG,发现很多错误且很慢
应用日志很慢:
4.4之前版本在源端遇到非常多更新接着删除操作导致目标端应用OPLOG很慢.
目标端对应逻辑:
更新失败--->去源端拉取同样失败-->本地填充--->接着删除--->串行操作--->很慢--->直到应用日志到T1时之后才恢复正常,4.4版本开始改变逻辑不去源端拉取.
对应原因:
因为在初始化全量时,源端还在进行DML操作,但克隆复制不是基于一致性拉取导致读到这个集合时数据已经修改删除.类似MYSQL DUMP可以基于一致性来备份做备库.虽然mongodump支持oplog备份
【3.4版本日志】
failed to apply update:op: "u", ns: "xiaoxu.xiaoxu",
o2: { _id: ObjectId('638716413bb56394afb29297') },
o: { $set: { curT: new }
adding missing object
missing object not found on source.
presumably deleted later in oplog
o2: { _id: ObjectId('638716413bb56394afb29297') }
o firstfield: $set
【4.2版本日志】
failed to apply update: txnNumber: 1, o: { $v: 1, $set:
{ name: "xiaoxu" } } }
Fetching missing document: o: { $v: 1, $set: { name: "xiaoxu" } },
o2: { _id: { w: 3, i: 21142526 } },
Missing document not found on source; presumably deleted later in
oplog.
o first field: { $v: 1, $set: { name: "xiaoxu" } },
o2: { _id: { w: 3, i: 21142526 } }
【6.0版本日志】
"msg":"Update of non-mod failed","attr":
"op":"u","ns":"SPOCDB.Sinitsync","ui":
{"$uuid":"81779492-6d5c-4dfd-afe2-edb8770ce12e"},
"o":{"$v":2,"diff":{"u":{"name":"Sinitsync"},
"i":{"comment":"testid2"}}},"o2":{"_id":2},"stmtId":0,
"ts":{"$timestamp":{"t":1670470302,"i":1}},"t":6,"v":2,
"wall":{"$date":"2022-12-08T03:31:42.497Z"},
"prevOpTime":{"ts":{"$timestamp":{"t":0,"i":0}},"t":-1}}}}}
3、对源端连接影响【4.4之前版本】
在全量初始化拉取数据阶段,数据先更新然后被删除,在拉取的时是流式读取表数据,在读取的时数据已经被删除,所以拉到目标端同样不存在,在全量拉取完成后,应用这段时间产生oplog进行一致性恢复时,找不到记录就提示 failed to apply update,为了保证幂等操作,此时去源端拉取数据,同样拉取不到.missing object not found on source. presumably deleted later in oplog.这个是串行操作,所以更慢.此时如果源端配置连接数少,那么此时去源端拉取数据时。可能会导致无法获取连接数导致拉取失败的情况.
【源端--都是目标库的短连接】
2022-12-05T16:54:12.262+0800 I ACCESS [conn12208300] Successfully authenticated as principal __system on local
2022-12-05T16:54:12.262+0800 I - [conn12208300] end connection 100.15.30.84:59190 (2391 connections now open)
2022-12-05T16:54:12.363+0800 I - [conn12073894] end connection 100.15.30.84:61902 (2390 connections now open)
2022-12-05T16:54:12.444+0800 I NETWORK [thread2] connection accepted from 100.15.30.84:59194 #12208301 (2390 connections now open)
2022-12-05T16:54:12.444+0800 I NETWORK [conn12208301] received client metadata from 100.15.30.84:59194 conn12208301: { driver: { name: "MongoDB Internal Client", version: "3.4.4" }, os: { type: "Linux", name: "CentOS release 6.9 (Final)", architecture: "x86_64", version: "Kernel 2.6.32-696.el6.x86_64" } }
2022-12-05T16:54:12.448+0800 I ACCESS [conn12208301] Successfully authenticated as principal __system on local
2022-12-05T16:54:12.449+0800 I - [conn12208301] end connection 100.15.30.84:59194 (2390 connections now open)
4、应对方案
【基于文件初始化复制过程】
备注:使用这种方式相对逻辑复制本身快很多,需要结合实际场景来使用.使用企业版6.0新特性基于文件复制的初始化方案.使用6.0非企业版本设置这个参数会提示:"No such initial sync method was available. Falling back to logical initial sync."会自动切换逻辑复制模式.
源端DB:大约3.5G
参数配置与逻辑无特别差异,主要在于initialSyncMethod方式:
setParameter:
initialSyncMethod: fileCopyBased
速度:相比逻辑复制时间降低75%,这个性能提升可观.
基于文件复制:.totalInitialSyncElapsedMillis:28180(28s)
逻辑复制:totalInitialSyncElapsedMillis:118281(118s)
【使用正确的姿势来初始化实例】
1、PSS新增实例场景
对于PSS场景新增实例,可以采用逻辑或者基于文件复制来初始化新实例.都需要执行rs.add.那么此时关注MajorityCount变化对应用以及主库的影响.这里rs.add增加需要配置votes:0以及priority:0.否则可能会遇到坑.
使用采用db.fsyncLock()来复制一个从库,然后把新节点直接加入(没有指定votes:0以及priority:0参数),那么此时writeMajorityCount变成3,此时2个节点都延迟,导致无法满足这个指标,同样对write,readConcern以及主库内存压力都有影响.
使用逻辑初始化方式,然后把新节点直接加入(没有指定votes:0以及priority:0参数),此时出现节点宕机一个实例,同样会导致以上问题.
2、PSS初始化其中1个S实例.
通常情况来说使用逻辑初始化或者基于LVM快照方式更合适(6.0企业版可以使用基于文件复制),如采用db.syncLock()方式,在没有更改默认参数情况下,此时WriteMajorityCount等于2则不满足,影响应用端w:majority以及readConcern.
同时对主库内存压力有影响(主库足够大影响明显). 如果非要使用db.syncLock方式也可以(一般不建议).也可以参考之前《PSA架构痛点文章中详细讲解》
3、PSA架构增加实例或者初始化实例
这个时候也需要特别小心 MajorityCount变化.否则给系统带来不必要的影响.
4、注意4.4针对startup2节点改变--经过实际测试是不准确,应该是从5.0版本开始才真正改变.如果没有经过验证,直接在生产上PSA架构直接使用逻辑初始化加节点,那么副本集会存在以上问题.所以不管版本指定votes:0以及priority:0更安全.
shard1 [direct: primary] test> db.version()
4.4.18
shard1 [direct: primary] test> rs.status()
{set: 'shard1',
majorityVoteCount: 2,writeMajorityCount: 2,
votingMembersCount: 2,writableVotingMembersCount: 2},
members: [
{_id: 0,name: '100.130.10.49:44001',health: 1,state: 1,
stateStr: 'PRIMARY',},
{_id: 1,name: '100.130.9.49:44001',health: 1,state: 5,
stateStr: 'STARTUP2'}
]}
shard1 [direct: primary] test> db.version()
5.0.3
shard1 [direct: primary] test> rs.status()
{set: 'shard1',
majorityVoteCount: 1,writeMajorityCount: 1,votingMembersCount: 1,
writableVotingMembersCount: 1,
},members: [
{_id: 0,name: '100.130.10.49:55001',health: 1,state: 1,
stateStr: 'PRIMARY',},
{ _id: 1, name: '100.130.9.49:55001', health: 1,state: 5,
stateStr: 'STARTUP2'}
]}
5、操作导致MajorityCount改变能否抢救
如果加节点或者初始化导致MajorityCount改变,可以通过修改节点votes和priority都设置为0,然后重新配置副本集信息,使得MajorityCount恢复正常,从而不影响正常write、readConcern等操作以及主库majority committed信息.
以下是4.2版本一个例子,增加节点导致MajorityCount改变,通过改变votes和priority
来恢复.
shard1:PRIMARY> rs.status()
{
"set" : "shard1",
"majorityVoteCount" : 3,
"writeMajorityCount" : 3
}
shard1:PRIMARY> rs.printSlaveReplicationInfo()
source: 100.130.99.150:42001
syncedTo: Fri Dec 09 2022 19:18:59 GMT+0800 (CST)
0 secs (0 hrs) behind the primary
source: 100.130.99.149:42001
syncedTo: Fri Dec 09 2022 19:11:33 GMT+0800 (CST)
446 secs (0.12 hrs) behind the primary
source: 100.130.100.150:42001
syncedTo: Fri Dec 09 2022 19:11:35 GMT+0800 (CST)
444 secs (0.12 hrs) behind the primary
shard1:PRIMARY>
db.test.insert({name:"xiaoxu"},{writeConcern:{w:"majority",
wtimeout:1000}})
WriteResult({
"nInserted" : 1,
"writeConcernError" : {
"code" : 64,
"codeName" : "WriteConcernFailed",
"errmsg" : "waiting for replication timed out",
"errInfo" : {
"wtimeout" : true
shard1:PRIMARY> cfg.members[3]
{
"_id" : 4,
"host" : "100.130.100.150:42001",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 0,
"slaveDelay" : NumberLong(0),
"votes" : 0
shard1:PRIMARY> rs.reconfig(cfg)
shard1:PRIMARY> db.test.insert({name:"xiaoxu"},
{writeConcern:{w:"majority",wtimeout:1000}})
WriteResult({ "nInserted" : 1 })
shard1:PRIMARY>
shard1:PRIMARY> rs.status()
{
"set" : "shard1",
"majorityVoteCount" : 2,
"writeMajorityCount" : 2
}
【总结】
1、MongoDB支持多种对实例进行初始化,分为逻辑与物理方式.根据实际情况来选择对应初始化方式同时需要关注MajorityCount改变对应用与DB的影响.
2、如因为误加节点导致性能问题,通过修改节点参数能够及时恢复原状从而降低对系统的影响.
3、增加节点或者节点宕机是如何影响应用以及DB的性能,可以参考前面的文章.
4、总体来说MongoDB初始化备节点简单、方便.3.4版本逻辑复制方式解决全量时间窗口超过OPLOG窗口时造成初始化失败问题.特别是云下企业版6.0支持在线的基于文件的初始化备库大大加快提升效率,对超大MongoDB实例来说更加友好.基于6.0企业版测试对于逻辑复制降低75%时间.