Skip to content

oplog各字段的解析

zhangst edited this page Dec 12, 2022 · 5 revisions

本文以4.2版本里面,一条事务对应的oplog进行举例,早于4.2的版本基本跟这个差别不大,有些字段是没有的。
文章的最后会介绍kafka通道中MongoShake吐出来的oplog格式。

各字段含义如下:

  • ts。操作的时间戳,64位表示,高32位是时间戳,低32位是计数累加。
  • t。election term。对应raft协议里面的term,每次发生节点down掉,新节点加入,主从切换,term都会自增。
  • h。操作的全局唯一id的hash结果。
  • v。oplog的版本字段。
  • op。具体操作类型。"i"表示插入,"d"表示删除,"u"表示更新,"c"表示是DDL操作,"n"表示是空消息(可以看作是心跳oplog)。
  • ns。命名空间。操作发生在哪个表上面。
  • o。具体的操作指令字段。对于op=i,这里表示插入的内容;op=d,这里表示要删除的文档;op=u,这里表示更新后的内容。
  • o2。查询字段,只存在于op=u的情况,指定要操作的文档。
  • ui。表的uuid。
  • b。upsert。如果是true表示是以upsert的方式插入,兼容3.6以前的版本。
  • wall。毫秒粒度墙上时钟。
  • fromMigrate。move chunk产生的oplog。
  • lsid。session id。在3.6之后,如果启动一个session并执行相应的修改操作,那么这些操作对应的oplog就会多一个这个lsid字段标识当前session。
  • txnNumber。事务id。每个事务都会对应一个txnNumber。
  • stmtId。
  • prevOpTime。
  • preImageOpTime。
  • postImageOpTime。

几个简单的操作对应的oplog举例

insert

向zz.test表插入数据{"kick":1}

{
    "ts" : Timestamp(1582277156, 1),
    "t" : NumberLong(1),
    "h" : NumberLong(0),
    "v" : 2,
    "op" : "i",
    "ns" : "zz.test",
    "ui" : UUID("20d9f949-cfc7-496e-a80e-32ba633701a8"),
    "wall" : ISODate("2020-02-21T09:25:56.570Z"),
    "o" : {
        "_id" : ObjectId("5e4fa224a6717632d6ee2e85"),
        "kick" : 1
    }
}

delete

从zz.test表中移除{"kick":1}

{
    "ts" : Timestamp(1582529794, 1),
    "t" : NumberLong(1),
    "h" : NumberLong(0),
    "v" : 2,
    "op" : "d",
    "ns" : "zz.test",
    "ui" : UUID("ee9b60d8-845f-42ff-989d-09018a730d60"),
    "wall" : ISODate("2020-02-24T07:36:34.063Z"),
    "o" : {
        "_id" : ObjectId("5e537cf27dc0f30426f01b77")
    }
}

update

普通update:db.test.update({"kick":1}, {"kick":10, "ok":true})

{
    "ts" : Timestamp(1582531841, 1),
    "t" : NumberLong(1),
    "h" : NumberLong(0),
    "v" : 2,
    "op" : "u",
    "ns" : "zz.test",
    "ui" : UUID("ee9b60d8-845f-42ff-989d-09018a730d60"),
    "o2" : {
        "_id" : ObjectId("5e5384f97dc0f30426f01b79")
    },
    "wall" : ISODate("2020-02-24T08:10:41.636Z"),
    "o" : {
        "_id" : ObjectId("5e5384f97dc0f30426f01b79"),
        "kick" : 10,
        "ok" : true
    }
}

以$set和$unset更新操作:

mgset-xxx:PRIMARY> db.test.find()
{ "_id" : ObjectId("5e5384f97dc0f30426f01b79"), "kick" : 10, "ok" : true }
mgset-xxx:PRIMARY> db.test.update({"kick":10}, {$set:{"plus_field":2}, $unset:{"ok":1}})
mgset-xxx:PRIMARY> db.test.find()
{ "_id" : ObjectId("5e5384f97dc0f30426f01b79"), "kick" : 10, "plus_field" : 2 }
{
    "ts" : Timestamp(1582533077, 2),
    "t" : NumberLong(1),
    "h" : NumberLong(0),
    "v" : 2,
    "op" : "u",
    "ns" : "zz.test",
    "ui" : UUID("ee9b60d8-845f-42ff-989d-09018a730d60"),
    "o2" : {
        "_id" : ObjectId("5e5384f97dc0f30426f01b79")
    },
    "wall" : ISODate("2020-02-24T08:31:17.681Z"),
    "o" : {
        "$v" : 1,
        "$unset" : {
            "ok" : true
        },
        "$set" : {
            "plus_field" : 2
        }
    }
}

replaceOne

普通replace:db.test.replaceOne({"kick" : 10}, {"kick":1, "ok":false})

{
    "ts": Timestamp(1670831918,1),
    "t": NumberLong(2),
    "h": NumberLong(0),
    "v": 2,
    "op": "u",
    "ns": "zz.test",
    "ui": UUID("5c45ab04-8eda-49ec-998a-434c3db167a6"),
    "o2": {
        "_id": ObjectId("6396de5c5ee474a67d3cb436")
    },
    "wall": ISODate("2022-12-12T07:58:38.317Z"),
    "o": {
        "_id": ObjectId("6396de5c5ee474a67d3cb436"),
        "kick": 1,
        "ok": false
    }
}

DDL

删表:mgset-xxx:PRIMARY> db.test.drop()

{
    "ts" : Timestamp(1582534135, 1),
    "t" : NumberLong(1),
    "h" : NumberLong(0),
    "v" : 2,
    "op" : "c",
    "ns" : "zz.$cmd",
    "ui" : UUID("ee9b60d8-845f-42ff-989d-09018a730d60"),
    "o2" : {
        "numRecords" : 3
    },
    "wall" : ISODate("2020-02-24T08:48:55.148Z"),
    "o" : {
        "drop" : "test"
    }
}

rename重命名: mgset-22785363:PRIMARY> db.test.renameCollection("test_collection_rename")

{
    "ts" : Timestamp(1582534922, 1),
    "t" : NumberLong(1),
    "h" : NumberLong(0),
    "v" : 2,
    "op" : "c",
    "ns" : "zz.$cmd",
    "ui" : UUID("da21cb04-a4c8-46ad-8b3c-3e8e9314f4b5"),
    "wall" : ISODate("2020-02-24T09:02:02.685Z"),
    "o" : {
        "renameCollection" : "zz.test",
        "to" : "zz.test_collection_rename",
        "stayTemp" : false
		"dropTarget": UUID("52c1c147-2408-4d96-9d0f-889a759ab079"), // field only exists when target collection is exists
    }
}

删库:mgset-22785363:PRIMARY> db.dropDatabase()

{
    "ts" : Timestamp(1582535589, 4),
    "t" : NumberLong(1),
    "h" : NumberLong(0),
    "v" : 2,
    "op" : "c",
    "ns" : "zz.$cmd",
    "wall" : ISODate("2020-02-24T09:13:09.165Z"),
    "o" : {
        "dropDatabase" : 1
    }
}

一个事务DDL:

{
    "ts" : Timestamp(1530696933, 1), 
    "t" : NumberLong(1), 
    "h" : NumberLong("4217817601701821530"), 
    "v" : 2,
    "op" : "c", 
    "ns" : "admin.$cmd", 
    "wall" : ISODate("2018-07-04T09:35:33.549Z"),
    "lsid" : {
        "id" : UUID("e675c046-d70b-44c2-ad8d-3f34f2019a7e"), 
        "uid" : BinData(0, "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=")
    }, 
    "txnNumber" : NumberLong(0), 
    "stmtId" : 0,
    "prevOpTime" : {
        "ts" : Timestamp(0, 0), 
        "t" : NumberLong(-1) 
    }, 
    "o" : {
        "applyOps" : [
            {
                "op" : "i", 
                "ns" : "test.coll2", 
                "ui" : UUID("a49ccd80-6cfc-4896-9740-c5bff41e7cce"), 
                "o" : {
                    "_id" : ObjectId("5b3c94d4624d615ede6097ae"), 
                    "x" : 20000
                }
            }, 
            {
                "op" : "i", 
                "ns" : "test.coll3", 
                "ui" : UUID("31d7ae62-fe78-44f5-ba06-595ae3b871fc"), 
                "o" : {
                    "_id" : ObjectId("5b3c94d9624d615ede6097af"), 
                    "x" : 20000
                }
            }
        ]
    }
}

MongoShake中对应的oplog格式

MongoShake的json格式吐出的oplog基本跟原生的oplog格式一样,但是多了几个用于标记的字段:UniqueIndexesUpdates, RawSize, SourceId,这几个字段用户不需要关心,将会在2.4版本去除,guk是阿里云针对内核修改支持的字段,开源用户也不需要关心。 下面以2个用户吐出的Oplog为例挨个解释每个field的意义和格式:

示例1

{
	"ts": 6799808956219785217, // 64位时间戳,其格式就是类似"Timestamp(1582277156, 1)",右移32位可以得到32位的秒级时间戳
	"op": "i", // 插入操作
	"g": "", // 阿里云内核独有,开源版本请忽略
	"ns": "dict.test", // 针对db=dict,collection=test的操作
	"o": [{ // 具体插入的文档内容,Name表示key,Value表示value,翻译过来下面这个就是:o:{"_id": "5e5dc63842d2a398850d6222", "name": "zhangsan", "age": 18}
		"Name": "_id",
		"Value": "5e5dc63842d2a398850d6222"
	}, {
		"Name": "name",
		"Value": "zhangsan"
	}, {
		"Name": "age",
		"Value": 18
	}],
	"o2": null, // o2字段
	"uk": null, // 阿里云内核独有,开源版本请忽略
	"lsid": null, // session字段
	"fromMigrate": false, // move chunk字段
	"UniqueIndexesUpdates": null, // MongoShake标记字段,2.4以前版本请忽略
	"RawSize": 131, // MongoShake标记字段,2.4以前版本请忽略
	"SourceId": 0 // MongoShake标记字段,2.4以前版本请忽略
}

示例2

{
	"ts": 6799816725815623681, // 同上个示例,时间戳
	"op": "u", // update操作
	"g": "", // 同上个示例,忽略
	"ns": "dict.test", // 同上个示例, namespace
	"o": [{ // 相当于o:{$set:{"content": "来个中文咋样---"}}
		"Name": "$set",
		"Value": [{
			"Name": "content",
			"Value": "来个中文咋样---"
		}]
	}],
	"o2": { // query字段,相当于: o2:{"_id": "5e5dcb5942d2a398850d6224"}
		"_id": "5e5dcb5942d2a398850d6224"
	},
	"uk": null, // 同上个示例,忽略
	"lsid": null, // 同上个示例
	"fromMigrate": false, // 同上个示例
	"UniqueIndexesUpdates": null, // 同上个示例,忽略
	"RawSize": 154, // 同上个示例,忽略
	"SourceId": 0 // 同上个示例,忽略
}

需要注意的是,o字段的格式与其他字段不同,o字段是bson.D的结构(数组嵌套字段),o2是bson.M结构(字典)

参考

oplog_entry.idl