共计 3664 个字符,预计需要花费 10 分钟才能阅读完成。
在学习mongodb时,会遇到两个mongodb日志,oplog和journal log。这两者有点类似mysql的binlog和redo log,而此处则记录下oplog的一些知识
什么是oplog?
oplog是mongodb用来提供副本集群复制功能的集合,它位于local库下,是一个定长集合(capped col),Secondary就是通过查看Primary的oplog集合来进行复制的,当然每个角色节点都有oplog,记录着同步过来的数据信息,这样就可以每个节点都可以作为同步源给其他节点使用,减轻Primary的压力
shard03:PRIMARY> use local
switched to db local
shard03:PRIMARY> show collections;
oplog.rs
replset.election
replset.initialSyncId
replset.minvalid
replset.oplogTruncateAfterPoint
startup_log
system.replset
system.rollback.id
oplog数据大小
oplog因为是一个定长集合,所以只能保存特定数量的操作日志,默认是当前节点磁盘的5%(默认最小为1G,最大为50G),可以通过在mongodb.conf中的oplogSize参数来设置
shard03:PRIMARY> db.getReplicationInfo()
{
"logSizeMB" : 1576.527099609375,
"usedMB" : 1410.88,
"timeDiff" : 253290,
"timeDiffHours" : 70.36,
"tFirst" : "Fri Nov 11 2021 00:06:26 GMT+0800 (CST)",
"tLast" : "Sun Nov 12 2021 22:27:56 GMT+0800 (CST)",
"now" : "Sun Nov 12 2021 22:28:06 GMT+0800 (CST)"
}
对于oplogSize该如何设置?因为定长集合是一个环状结构,数据超过设定大小,新数据则会覆写最老的数据,如果我们Secondary节点来不及同步,数据就被覆写了,那Secondary节点就会处于RECOVERING状态。如果设置过大,则必然浪费空间~~~
oplog的增长速度
上面我们知道oplog的过大浪费空间,过小容易造成Secondary节点RECOVERING,怎么设置,需要根据实际情况进行选择,这里就可以通过业务场景来判断。首先我们需要知道oplog是如何产生的?
当Primary进行写操作的时候,会将这些写操作记录写入到Primary的oplog中,而后Seconday才会从oplog中复制同步。一般情况下,Primary每分钟写入1KB数据,则oplog也是1KB写入每分钟。但是如果写操作是批量写或删,则会产生多条oplog,比如一次性增加或删除10条,那么oplog则会产生10条日志分别记录每条的操作
shard03:PRIMARY> db.users.insertMany([{"name":"张三","age":NumberInt(10),"sex":"男"},{"name":"李四","age":NumberInt(11),"sex":"男"}])
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("640de5e8ebbeb231f23dcd5d"),
ObjectId("640de5e8ebbeb231f23dcd5e")
]
}
上面一次插入两条,查下此时oplog被拆成两条
shard03:PRIMARY> db.oplog.rs.find({"ns": "xadocker.users"})
{ "op" : "i", "ns" : "xadocker.users", "ui" : UUID("a41a19d2-137e-4e48-954a-22b96d557256"), "o" : { "_id" : ObjectId("640de5e8ebbeb231f23dcd5d"), "name" : "张三", "age" : 10, "sex" : "男" }, "ts" : Timestamp(1678632424, 2), "t" : NumberLong(2), "v" : NumberLong(2), "wall" : ISODate("2023-03-12T14:47:04.455Z") }
{ "op" : "i", "ns" : "xadocker.users", "ui" : UUID("a41a19d2-137e-4e48-954a-22b96d557256"), "o" : { "_id" : ObjectId("640de5e8ebbeb231f23dcd5e"), "name" : "李四", "age" : 11, "sex" : "男" }, "ts" : Timestamp(1678632424, 3), "t" : NumberLong(2), "v" : NumberLong(2), "wall" : ISODate("2023-03-12T14:47:04.455Z") }
如果是更新呢?
shard03:PRIMARY> use xadocker
switched to db xadocker
shard03:PRIMARY> db.users.update({"sex":"男"}, {"$inc":{"age":NumberInt(1)}}, false, true)
WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })
shard03:PRIMARY> db.oplog.rs.find({"ns": "xadocker.users", "op":"u"}).pretty()
{
"op" : "u",
"ns" : "xadocker.users",
"ui" : UUID("a41a19d2-137e-4e48-954a-22b96d557256"),
"o" : {
"$v" : 1,
"$set" : {
"age" : 11
}
},
"o2" : {
"_id" : ObjectId("640de5e8ebbeb231f23dcd5d")
},
"ts" : Timestamp(1678632798, 1),
"t" : NumberLong(2),
"v" : NumberLong(2),
"wall" : ISODate("2021-11-12T14:53:18.515Z")
}
{
"op" : "u",
"ns" : "xadocker.users",
"ui" : UUID("a41a19d2-137e-4e48-954a-22b96d557256"),
"o" : {
"$v" : 1,
"$set" : {
"age" : 12
}
},
"o2" : {
"_id" : ObjectId("640de5e8ebbeb231f23dcd5e")
},
"ts" : Timestamp(1678632798, 2),
"t" : NumberLong(2),
"v" : NumberLong(2),
"wall" : ISODate("2021-11-12T14:53:18.522Z")
}
从上面可以可以看到批量更新操作,最后也是追条生成oplog,而且在oplog中被记录为$set
,所以oplog的replay是可以多次执行的,具有幂等性。
到这里估计你就能想到,该参考什么去设置oplogSize的大小了,对于参数的调整不是设置完后就结束了,在博主的运维生涯来看是持续的,因为业务是会一直改变的~~~,没有单一的读,也没有单一的写~~
oplog数据结构
{
"op" : "u",
"ns" : "xadocker.users",
"ui" : UUID("a41a19d2-137e-4e48-954a-22b96d557256"),
"o" : {
"$v" : 1,
"$set" : {
"age" : 12
}
},
"o2" : {
"_id" : ObjectId("640de5e8ebbeb231f23dcd5e")
},
"ts" : Timestamp(1678632798, 2),
"t" : NumberLong(2),
"v" : NumberLong(2),
"wall" : ISODate("2021-11-12T14:53:18.522Z")
}
- ts: 8字节的时间戳,由4字节unix timestamp + 4字节自增计数表示。这个值很重要,在primary故障后选举primary时,会选择ts最大的那个secondary作为新primary
- op:1字节的操作类型
- “i”: insert
- “u”: update
- “d”: delete
- “c”: db cmd
- “db”:声明当前数据库 (其中ns 被设置成为=>数据库名称+ ‘.’)
- “n”: no op,即空操作,其会定期执行以确保时效性
- ns:操作所在的namespace
- o:操作所对应的document,即当前操作的内容(比如更新操作时要更新的的字段和值)
- o2: 在执行更新操作时的where条件,仅限于update时才有该属性