oplog使用固定大小集合记录了数据库中全部修改操做的操做日志(新增、修改和删除,无查询),mongodb收到修改请求后,先在主节点(Primary)执行请求,再把操做日志保存到oplog表中,其余从节点(Secondary)到主节点拉取oplog并在异步进程中应用这些操做,从而达到主从数据的一致性。复制组内的全部节点都会保存一份oplog(集合名local.oplog.rs),这让他们能够保持一样的数据库状态。mongodb
为了提升同步效率,全部复制组成员都会向其余成员发送保活报文(pings),任意从节点能够从其余成员节点同步oplog(便可以从主节点同步,也能够从从节点同步)。oplog中的操做都是幂等的,即oplog中的某个操做日志在目标数据库中应用一次或者屡次,其结果都是同样的。数据库
主从同步示意图以下(客户端写数据到主节点,从节点从主节点同步oplog并应用到本节点):异步
当你首次启动复制组节点时,在你未指定oplog大小时,mongodb会使用默认大小来建立oplog。oop
对于Unix和Windows系统来讲,默认大小和存储引擎的对应关系以下:ui
存储引擎类型 | oplog大小 | 下限 | 上限 |
内存 | 物理内存的5% | 50MB | 50GB |
WiredTiger | 空闲磁盘的5% | 990MB | 50GB |
(注意,最新4.4版本的mongodb移除了MMAP类型存储引擎的支持。)spa
对于64位maxOS系统来讲,参照使用的存储引擎类型,该默认大小是192MB(物理内存或者磁盘空间),以下:日志
存储引擎类型 | oplog大小 |
内存 | 192MB物理内存 |
WiredTiger | 192MB的磁盘空间 |
大部分状况下,oplog的默认大小是足够的。举个例子,若是5%的磁盘空间存储了最近24小时的操做日志,此时若是某个从节点的日志同步时间差超过24小时时,从节点将中止同步oplog,并将自身的状态从“Secondery”切换到“STALE”。固然,在实际的运行环境中,大部分复制组成员的负载会低一些,他们的oplog中也会持有更长时间段的日志。code
若是你预测到你的复制组的工做负载属于如下的模式,你须要建立比默认值更大一些的oplog。相反的,若是你的应用大部分状况下是读操做,只有小部分的写操做,那么更小一些的oplog也是知足须要的。blog
下面的工做负载可能须要更大一些的oplog进程
为了知足oplog的幂等性,单次操做更新多条记录时,mongodb会记录多条操做日志到oplog中,这种场景就须要使用大量的oplog的空间,虽然此时数据大小或者磁盘大小并无相应的增长那么多。
若是你的删除操做请求量和插入操做的请求量大体至关时,数据库在磁盘空间消耗方面不会有明显增加,可是操做日志的大小会很是巨大。
若是工做负载的大部分操做都是原文档更新,此时虽然不会增长数据库中文档的数量,可是数据库须要记录大量的操做日志。
若是要查看oplog的状态,包含记录条数和时间范围,能够使用"rs.printReplicationInfo() "命令,以下:
MongoDB Enterprise repa:PRIMARY> rs.printReplicationInfo() configured oplog size: 1024MB // oplog大小是1024MB log length start to end: 867353secs (240.93hrs) // 第一条和最后一条日志的时间差是240.93小时 oplog first event time: Wed Jul 07 2021 20:24:57 GMT+0800 oplog last event time: Sat Jul 17 2021 21:20:50 GMT+0800 now: Sat Jul 17 2021 21:20:56 GMT+0800
从前面知道oplog是存储在数据库local中,表名为“oplog.rs”,经过查询命令看一下oplog的数据格式:
db.oplog.rs.find({"ns":"test.users"}).limit(1) // ns字段指明查询对数据库test中users表的操做日志 { "ts": Timestamp(1625660877, 2), // 日志的操做时间戳,第一个数字是时间戳,单位秒,第二个数字是当前秒的第2个操做 "t": NumberLong(2), "h": NumberLong("5521980394145765083"), "v": 2, "op": "i", // i表示insert,u表示update,d表示delete,c 表示的是数据库的命令,好比建表,n表示noop,即空操做 "ns": "test.users", // 命名空间,即数据库和集合名称 "ui": UUID("edabbd93-76eb-42be-b54a-cdc29eb1f267"), // 链接到mongodb的客户端会话id "wall": ISODate("2021-07-07T12:27:57.689Z"), // 操做执行时间,utc时间 "o": { // 操做的内容,对于不一样的op类型,其格式不尽相同 "_id": ObjectId("60e59dcd46db1fb4605f8b18"), "name": "1" } }
前面分析oplog日志格式的时候,查看了一条insert操做对应的日志,就再也不赘述,下面再看下delete和update对应的日志格式(find不会产生oplog)。
首先插入三条记录:
MongoDB Enterprise repa:PRIMARY> use test
switched to db test
MongoDB Enterprise repa:PRIMARY> db.users.insert({"name":"张三","age":NumberInt(10),"sex":"男"})
WriteResult({ "nInserted" : 1 })
MongoDB Enterprise repa:PRIMARY> db.users.insert({"name":"李四","age":NumberInt(11),"sex":"男"})
WriteResult({ "nInserted" : 1 })
MongoDB Enterprise repa:PRIMARY> db.users.insert({"name":"王五","age":NumberInt(12),"sex":"男"})
WriteResult({ "nInserted" : 1 })
MongoDB Enterprise repa:PRIMARY> db.users.find()
{ "_id" : ObjectId("60f2e11b0d98dc3b374199de"), "name" : "张三", "age" : 10, "sex" : "男" }
{ "_id" : ObjectId("60f2e11e0d98dc3b374199df"), "name" : "李四", "age" : 11, "sex" : "男" }
{ "_id" : ObjectId("60f2e11e0d98dc3b374199e0"), "name" : "王五", "age" : 12, "sex" : "男" }
执行delete操做,匹配条件是{"sex":"男"},即删除全部性别为男的记录:
MongoDB Enterprise repa:PRIMARY> db.users.remove({"sex":"男"}) WriteResult({ "nRemoved" : 3 }) MongoDB Enterprise repa:PRIMARY> db.users.find()
MongoDB Enterprise repa:PRIMARY>
能够看到,一条删除命令删除了三条记录,对应的oplog是什么呢,来,查一下:
MongoDB Enterprise repa:PRIMARY> use local switched to db local MongoDB Enterprise repa:PRIMARY> db.oplog.rs.find({"ns":"test.users","op":"d","wall":{"$gt":ISODate("2021-07-17T13:50:57.689Z")}}) { "ts" : Timestamp(1626530154, 1), "t" : NumberLong(2), "h" : NumberLong("5834731856459959506"), "v" : 2, "op" : "d", "ns" : "test.users", "ui" : UUID("edabbd93-76eb-42be-b54a-cdc29eb1f267"), "wall" : ISODate("2021-07-17T13:55:54.424Z"), "o" : { "_id" : ObjectId("60f2e11b0d98dc3b374199de") } } { "ts" : Timestamp(1626530154, 2), "t" : NumberLong(2), "h" : NumberLong("-2164276082472824844"), "v" : 2, "op" : "d", "ns" : "test.users", "ui" : UUID("edabbd93-76eb-42be-b54a-cdc29eb1f267"), "wall" : ISODate("2021-07-17T13:55:54.424Z"), "o" : { "_id" : ObjectId("60f2e11e0d98dc3b374199df") } } { "ts" : Timestamp(1626530154, 3), "t" : NumberLong(2), "h" : NumberLong("3834858247238363179"), "v" : 2, "op" : "d", "ns" : "test.users", "ui" : UUID("edabbd93-76eb-42be-b54a-cdc29eb1f267"), "wall" : ISODate("2021-07-17T13:55:54.424Z"), "o" : { "_id" : ObjectId("60f2e11e0d98dc3b374199e0") } } MongoDB Enterprise repa:PRIMARY>
从上能够看到,一条删除命令,在oplog中记录了三条日志,下面分析其中的一条:
{ "ts": Timestamp(1626530154, 1), "t": NumberLong(2), "h": NumberLong("5834731856459959506"), "v": 2, "op": "d", // 删除操做 "ns": "test.users", // 数据库是test,集合是users "ui": UUID("edabbd93-76eb-42be-b54a-cdc29eb1f267"), "wall": ISODate("2021-07-17T13:55:54.424Z"), "o": { // 待删除记录的_id "_id": ObjectId("60f2e11b0d98dc3b374199de") } }
从上面日志分析能够获得结论:
用户的一次删除请求,若是删除了N条记录,那么oplog中将记录N条日志,日志中会记录待删除记录的“_id”字段,与用户的删除请求的参数无关。
下面再看下更新操做对应的oplog的日志数量和格式。
首先插入三条记录:
MongoDB Enterprise repa:PRIMARY> use test switched to db test MongoDB Enterprise repa:PRIMARY> MongoDB Enterprise repa:PRIMARY> db.users.insert({"name":"张三","age":NumberInt(10),"sex":"男"}) WriteResult({ "nInserted" : 1 }) MongoDB Enterprise repa:PRIMARY> db.users.insert({"name":"李四","age":NumberInt(11),"sex":"男"}) WriteResult({ "nInserted" : 1 }) MongoDB Enterprise repa:PRIMARY> db.users.insert({"name":"王五","age":NumberInt(12),"sex":"男"}) WriteResult({ "nInserted" : 1 }) MongoDB Enterprise repa:PRIMARY> db.users.find() { "_id" : ObjectId("60f2e2db0d98dc3b374199e1"), "name" : "张三", "age" : 10, "sex" : "男" } { "_id" : ObjectId("60f2e2db0d98dc3b374199e2"), "name" : "李四", "age" : 11, "sex" : "男" } { "_id" : ObjectId("60f2e2dc0d98dc3b374199e3"), "name" : "王五", "age" : 12, "sex" : "男" }
再执行更新操做:
MongoDB Enterprise repa:PRIMARY> db.users.update({"sex":"男"}, {"$inc":{"age":NumberInt(1)}}, false, true) WriteResult({ "nMatched" : 3, "nUpserted" : 0, "nModified" : 3 }) MongoDB Enterprise repa:PRIMARY> db.users.find() { "_id" : ObjectId("60f2e2db0d98dc3b374199e1"), "name" : "张三", "age" : 11, "sex" : "男" } { "_id" : ObjectId("60f2e2db0d98dc3b374199e2"), "name" : "李四", "age" : 12, "sex" : "男" } { "_id" : ObjectId("60f2e2dc0d98dc3b374199e3"), "name" : "王五", "age" : 13, "sex" : "男" }
从返回结果能够看到,更新操做执行成功,并更新了三条记录,下面看下oplog的日志:
MongoDB Enterprise repa:PRIMARY> use local switched to db local MongoDB Enterprise repa:PRIMARY> db.oplog.rs.find({"ns":"test.users","op":"u","wall":{"$gt":ISODate("2021-07-17T13:50:57.689Z")}}) { "ts" : Timestamp(1626530575, 1), "t" : NumberLong(2), "h" : NumberLong("-6359278368726841648"), "v" : 2, "op" : "u", "ns" : "test.users", "ui" : UUID("edabbd93-76eb-42be-b54a-cdc29eb1f267"), "o2" : { "_id" : ObjectId("60f2e2db0d98dc3b374199e1") }, "wall" : ISODate("2021-07-17T14:02:55.319Z"), "o" : { "$v" : 1, "$set" : { "age" : 11 } } } { "ts" : Timestamp(1626530575, 2), "t" : NumberLong(2), "h" : NumberLong("-4351658862590633053"), "v" : 2, "op" : "u", "ns" : "test.users", "ui" : UUID("edabbd93-76eb-42be-b54a-cdc29eb1f267"), "o2" : { "_id" : ObjectId("60f2e2db0d98dc3b374199e2") }, "wall" : ISODate("2021-07-17T14:02:55.319Z"), "o" : { "$v" : 1, "$set" : { "age" : 12 } } } { "ts" : Timestamp(1626530575, 3), "t" : NumberLong(2), "h" : NumberLong("5911110003695351597"), "v" : 2, "op" : "u", "ns" : "test.users", "ui" : UUID("edabbd93-76eb-42be-b54a-cdc29eb1f267"), "o2" : { "_id" : ObjectId("60f2e2dc0d98dc3b374199e3") }, "wall" : ISODate("2021-07-17T14:02:55.319Z"), "o" : { "$v" : 1, "$set" : { "age" : 13 } } }
和delete相似,update操做也是产生了三条日志,选第一条分析:
{ "ts": Timestamp(1626530575, 1), "t": NumberLong(2), "h": NumberLong("-6359278368726841648"), "v": 2, "op": "u", // 更新操做 "ns": "test.users", // 数据库test,集合是users "ui": UUID("edabbd93-76eb-42be-b54a-cdc29eb1f267"), "o2": { // 更新操做的查询条件,使用的记录的_id "_id": ObjectId("60f2e2db0d98dc3b374199e1") }, "wall": ISODate("2021-07-17T14:02:55.319Z"), "o": { // 更新操做的更新内容,原始的inc操做符转变为set操做符,能够知足幂等性 "$v": 1, "$set": { "age": 11 } } }
从上面日志分析能够获得结论:
用户的一次更新请求,若是更新了N条记录,那么oplog中将记录N条日志,日志中记录待更新记录的“_id”字段为查询条件,更新操做使用的是set操做符,并非用户的更新操做符。
从上面的delete和update操做对应的oplog日志分析能够看出,oplog记录的不是用户的原始命令,而是对应的逻辑命令,经过这种方式能够知足oplog的幂等性,可是也会衍生出可能产生大量oplog记录的问题,须要用户根据业务模型的须要,来选择合适的oplog大小。