如果你的 MongoDB 是使用 replication,那你会需要知道什麽是 oplog
;如果你的 MongoDB 是只有单一个节点,那就暂时还不需要理解。
oplog 是用来同步主要节点与次节点资料用的。例如我们写入了一笔资料,而这笔资料的异动资讯也会被写入 oplog 中,次节点会主动去主节点的 oplog collection 内执行find
, getMore
拿到需要同步的资料,再透过後续抄写机制回次节点,并告诉主节点最後同步时间。
MongoDB 同步机制中,来源不一定是主节点,例如这是我们预期的
但实际上可以是
这样做的目的是减轻主节点的压力。这个功能叫做 chainingAllowed
,在 rs.conf()
可以看到,预设为 true
。
各个次节点会以 heartbeat
方式确认互相彼此存活,以便进行资料同步。
首先你必须於本机建立 replica set,可以参考之前的文章来建立测试环境。
连上任一节点後输入 rs.printReplicationInfo()
,预期会取得以下结果:
ith2021-rs:SECONDARY> rs.printReplicationInfo()
configured oplog size: 2300.437744140625MB
log length start to end: 6203secs (1.72hrs)
oplog first event time: Sun Sep 12 2021 16:08:52 GMT+0800 (CST)
oplog last event time: Sun Sep 12 2021 17:52:15 GMT+0800 (CST)
now: Sun Sep 12 2021 17:52:24 GMT+0800 (CST)
输入以下指令: db.printSlaveReplicationInfo()
ith2021-rs:SECONDARY> db.printSlaveReplicationInfo()
source: mongo_node1:27666
syncedTo: Sun Sep 12 2021 17:54:25 GMT+0800 (CST)
0 secs (0 hrs) behind the primary
source: mongo_node2:27667
syncedTo: Sun Sep 12 2021 17:54:25 GMT+0800 (CST)
0 secs (0 hrs) behind the primary
0 secs (0 hrs) behind the primary
这段话的意思是跟主节点资料落差有多少秒,以上面的例子来说就是完全同步的意思。在後面我们会有一些测试方式来看数据的变化。
完整资讯是
ith2021-rs [direct: primary] local> db.getReplicationInfo()
{
logSizeMB: 130000.0999994278,
usedMB: 0.1,
timeDiff: 8474,
timeDiffHours: 2.35,
tFirst: 'Sun Sep 12 2021 16:08:52 GMT+0800 (台北标准时间)',
tLast: 'Sun Sep 12 2021 18:30:06 GMT+0800 (台北标准时间)',
now: 'Sun Sep 12 2021 18:30:14 GMT+0800 (台北标准时间)'
}
*Note: 主节点与次节点预设的 logSizeMB
是不同大小的
*Note2: db.getReplicationInfo()
, db.printReplicationInfo()
结果都是一样的,只是显示格式不同
第一个方法是於启动的时候加上设定值,而单位是 MB
,如下:
--oplogSize = 10
这样就是设定为 10MB 大小。
第二个方法是启动後的修改,输入以下指令:
db.adminCommand({replSetResizeOplog:1, size: 123456})
单位是 MB
修改比预设还小的值
ith2021-rs [direct: primary] local> db.getReplicationInfo().logSizeMB
6340
ith2021-rs [direct: primary] local> db.adminCommand({replSetResizeOplog:1, size: 1000.1})
{
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1631443194, i: 1 }),
signature: {
hash: Binary(Buffer.from("0000000000000000000000000000000000000000", "hex"), 0),
keyId: Long("0")
}
},
operationTime: Timestamp({ t: 1631443194, i: 1 })
}
ith2021-rs [direct: primary] local> db.getReplicationInfo().logSizeMB
6649
可以看到回传虽然 ok 为 1,但并没有任何改变。
修改比预设还大的值
ith2021-rs [direct: primary] local> db.adminCommand({replSetResizeOplog:1, size: 7899.1})
{
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1631443324, i: 1 }),
signature: {
hash: Binary(Buffer.from("0000000000000000000000000000000000000000", "hex"), 0),
keyId: Long("0")
}
},
operationTime: Timestamp({ t: 1631443324, i: 1 })
}
ith2021-rs [direct: primary] local> db.getReplicationInfo().logSizeMB
7899.099999427795
首先,oplog 满了,会先把最旧的删除,就像是 FIFO 的概念。
所谓的满,有可能是容量满,或是档案数超过喔!
当 oplog 满了,会触发 full resync,强制让次节点完全同步 oplog 内容後,才会从 recovering
状态变回正常。
这篇文章将实际对 MongoDB 进行一些操作,接着查看 oplog 有何变化。
以下内容的操作步骤:
ith2021
ironman
{"op": "i"}
(细节後谈)ith2021-rs [direct: primary] local> use ith2021
switched to db ith2021
ith2021-rs [direct: primary] ith2021> db.ironman.insertOne({field:'iThome 2021 Winner'})
{
acknowledged: true,
insertedId: ObjectId("613ddc90a3c50f67ffc384cd")
}
ith2021-rs [direct: primary] ith2021> use local
switched to db local
ith2021-rs [direct: primary] local> db.oplog.rs.find({"op":"i"})
[
{
lsid: {
id: UUID("84a171ba-6dbe-47b0-8187-6775dacd1281"),
uid: Binary(Buffer.from("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "hex"), 0)
},
txnNumber: Long("2"),
op: 'i',
ns: 'ith2021.ironman',
ui: UUID("c227fbe2-bae6-4ee1-8a7a-8ea32c99edd3"),
o: {
_id: ObjectId("613ddc90a3c50f67ffc384cd"),
field: 'iThome 2021 Winner'
},
ts: Timestamp({ t: 1631444112, i: 1 }),
t: Long("1"),
v: Long("2"),
wall: ISODate("2021-09-12T10:55:12.886Z"),
stmtId: 0,
prevOpTime: { ts: Timestamp({ t: 0, i: 0 }), t: Long("-1") }
}
]
从上面的操作步骤可以看到,我们写入一笔资料後,oplog 也会有有一笔相同内容资讯的纪录,这个就是让次节点拿去同步的。上面的查询条件 op
是代表 operation 的意思,MongoDb很常使用,应该是不陌生;i
则代表 insert
,还有以下操作:
这边我们将第一个栏位更新(update)其内容,再查看 oplog:
ith2021-rs [direct: primary] ith2021> db.ironman.updateOne({_id:ObjectId("613ddc90a3c50f67ffc384cd")}, {$set:{"field":"iThome 2021 Winner - eplis"}})
ith2021-rs [direct: primary] local> db.oplog.rs.find({"op":"u"})
[
{
lsid: {
id: UUID("129cd660-1da1-4a2a-94ae-569d0b406ec3"),
uid: Binary(Buffer.from("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "hex"), 0)
},
txnNumber: Long("2"),
op: 'u',
ns: 'ith2021.ironman',
ui: UUID("c227fbe2-bae6-4ee1-8a7a-8ea32c99edd3"),
o: { '$v': 2, diff: { u: { field: 'iThome 2021 Winner - eplis' } } },
o2: { _id: ObjectId("613ddc90a3c50f67ffc384cd") },
ts: Timestamp({ t: 1631444513, i: 1 }),
t: Long("1"),
v: Long("2"),
wall: ISODate("2021-09-12T11:01:53.945Z"),
stmtId: 0,
prevOpTime: { ts: Timestamp({ t: 0, i: 0 }), t: Long("-1") }
}
]
再使用 Replace 语法去做更新,查看 oplog..
ith2021-rs [direct: primary] ith2021> db.ironman.replaceOne({_id: ObjectId("613ddc90a3c50f67ffc384cd")}, {field: 'iThome Winner', year: 2021, winner: 'eplis'})
ith2021-rs [direct: primary] local> db.oplog.rs.find({"op":"u"})
[
{
lsid: {
id: UUID("129cd660-1da1-4a2a-94ae-569d0b406ec3"),
uid: Binary(Buffer.from("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "hex"), 0)
},
txnNumber: Long("3"),
op: 'u',
ns: 'ith2021.ironman',
ui: UUID("c227fbe2-bae6-4ee1-8a7a-8ea32c99edd3"),
o: {
_id: ObjectId("613ddc90a3c50f67ffc384cd"),
field: 'iThome Winner',
year: 2021,
winner: 'eplis'
},
o2: { _id: ObjectId("613ddc90a3c50f67ffc384cd") },
ts: Timestamp({ t: 1631444709, i: 1 }),
t: Long("1"),
v: Long("2"),
wall: ISODate("2021-09-12T11:05:09.177Z"),
stmtId: 0,
prevOpTime: { ts: Timestamp({ t: 0, i: 0 }), t: Long("-1") }
}
]
可以看到同样属於 update 操作,但是魔鬼藏在细节里!!
update
只会存修改的栏位replace
整份文件都会写入 oplog确实很合理,但是如果是大量使用 replace 或是一个文件大小很大的话,会造成 oplog 大量被占用记忆体,进而导致缩短可用时间,再继续下去可能就会触发次节点 full resync。所以使用上一定要特别斟酌是否需要 replace。
本系列文章会同步发表於我个人的部落格 Pie Note
<<: [Day04 - UI/UX] 确认使用的 UI framework
>>: Re: 新手让网页 act 起来: Day04 - JSX
由於浏览器请求会自动包含与站点关联的任何凭据 多数站点继承了受害者的身份和特权,如Session c...
写程序的时候,我们常常需要「判断」某些条件,当条件成立、条件为"真"的时候,执行某一段程序码,而条件...
做完了这几天的JAVA分享。。。我说是分享啦,因为我没有厉害到可以教别人 恩,所以做完分享之後,我也...
如果单纯从学习Ruby再学习运用Rails开发网页专案,那可能还要再认识一些技能,对开发上能更有帮助...
这周接续上周进度 把真实资料喂进 SuccessCard.vue 罗~ #左侧预约功能(接续) 真实...