[MongoDB] oplog 분석

DataBase 2024. 5. 29. 17:31

 

MongoDB 7.0.6 기준

 

 

oplog는 MongoDB의 replicaSet에서 데이터 동기화를 위해 primary 노드에서 발생한 모든 데이터 변경 작업을 기록하여 secondary 노드로 전파하여 데이터의 일관성을 유지하기 위한 로그.

 

 

oplog의 일반적인 포맷은 아래와 같음.

// insertOne()에 대한 oplog 예제.
{
    // Logical Session ID (
    lsid: {
      id: UUID('72e4e59b-84d9-4aa0-9830-39173b08087f'),
      uid: Binary.createFromBase64('Y5mrDaxi8gv8RmdTsQ+1j7fmkr7JUsabhNmXAheU0fg=', 0)
    },
    // Transaction Number (세션 내에서 실행된 트랜잭션 번호. 동일 트랜잭션일 경우 같은 번호)
    txnNumber: Long('10'),
    // 실행된 명령 유형 (i=insert, u=update, d=delete, n=no-op, c=command)
    op: 'i',
    // namespace
    ns: 'my_service.users',
    // collection unique id
    ui: UUID('d2c39f44-ec4e-41e7-93f5-94abbd29cd8b'),
    // 실행된 주 object
    o: { _id: ObjectId('6656d168798ce72b3fdb83bc'), name: '백충덕', age: 99 },
    // query object. update의 경우 조건문에 해당
    o2: { name: '백충덕', age: 99, _id: ObjectId('6656d168798ce72b3fdb83bc') },
    // Statement ID. 트랜잭션 내에서 실행된 명령문 번호=순서
    stmtId: 0,
    // timestamp
    ts: Timestamp({ t: 1716965736, i: 171 }),
    // Term. replicaSet이 시작된 이후 몇 번의 선출(election)이 있었는지 나타내는 숫자
    t: Long('4'),
    // oplog 버전
    v: Long('2'),
    // ts의 t값을 알아보기 쉽게 ISODate()로 변환한 값
    wall: ISODate('2024-05-29T06:55:36.703Z'),
    // 트랜잭션 내에서 이전 작업의 ts
    prevOpTime: { ts: Timestamp({ t: 0, i: 0 }), t: Long('-1') }
}

 

 

 

 

 

op별 oplog.
service 라는 DB를 생성하고 users라는 collection 생성부터 각종 명령 실행마다의 예제임.

// 컬렉션 생성
// _________________________________________________________________________
db.createCollection('users', {
    validator:{
        $jsonSchema:{
            bsonType:'object',
            title:'shard key',
            required:['name','age'],
            properties:{
                name:{
                    bsonType:'string'
                },
                age:{
                    bsonType:'int'
                }
            }
        }
    }
})
 
// oplog
{
    op: 'c',
    ns: 'service.$cmd',
    ui: UUID('1cb735b8-a758-4708-b8a2-c45e07367143'),
    o: {
      create: 'users',
      validator: {
        '$jsonSchema': {
          bsonType: 'object',
          title: 'shard key',
          required: [ 'name', 'age' ],
          properties: { name: { bsonType: 'string' }, age: { bsonType: 'int' } }
        }
      },
      idIndex: { v: 2, key: { _id: 1 }, name: '_id_' }
    },
    ts: Timestamp({ t: 1716967002, i: 2 }),
    t: Long('4'),
    v: Long('2'),
    wall: ISODate('2024-05-29T07:16:42.914Z')
}
 
 
 
// 인덱스 생성
// _________________________________________________________________________
db.users.createIndex({name:1, age:1})
 
// oplog
{
    op: 'c',
    ns: 'service.$cmd',
    ui: UUID('a565958e-3734-4b67-8636-d670d2654900'),
    o: {
      createIndexes: 'users',
      v: 2,
      key: { name: 1, age: 1 },
      name: 'name_1_age_1'
    },
    ts: Timestamp({ t: 1716967885, i: 20 }),
    t: Long('4'),
    v: Long('2'),
    wall: ISODate('2024-05-29T07:31:25.060Z')
}
 
 
 
// sharding
// _________________________________________________________________________
sh.shardCollection('service.users', {name:1,age:1})
 
// oplog
{
    op: 'n',
    ns: 'service.users',
    ui: UUID('a565958e-3734-4b67-8636-d670d2654900'),
    o: { msg: { shardCollectionPrepare: 'service.users' } },
    o2: {
      shards: [ 'shard0001' ],
      shardCollectionPrepare: 'service.users',
      shardKey: { name: 1, age: 1 },
      unique: false,
      numInitialChunks: Long('0'),
      presplitHashedZones: false
    },
    ts: Timestamp({ t: 1716968001, i: 196 }),
    t: Long('4'),
    v: Long('2'),
    wall: ISODate('2024-05-29T07:33:21.817Z')
},
{
    op: 'n',
    ns: 'service.users',
    ui: UUID('a565958e-3734-4b67-8636-d670d2654900'),
    o: { msg: { shardCollection: 'service.users' } },
    o2: {
      shardCollection: 'service.users',
      shardKey: { name: 1, age: 1 },
      unique: false,
      numInitialChunks: Long('0'),
      presplitHashedZones: false
    },
    ts: Timestamp({ t: 1716968001, i: 200 }),
    t: Long('4'),
    v: Long('2'),
    wall: ISODate('2024-05-29T07:33:21.822Z')
}
 
 
 
 
// insertOne
// _________________________________________________________________________
db.users.insertOne({name:'백충덕', age:99})
 
// oplog
{
    lsid: {
      id: UUID('e573bc34-6e6c-4d27-b858-083de253605e'),
      uid: Binary.createFromBase64('Y5mrDaxi8gv8RmdTsQ+1j7fmkr7JUsabhNmXAheU0fg=', 0)
    },
    txnNumber: Long('1'),
    op: 'i',
    ns: 'service.users',
    ui: UUID('a565958e-3734-4b67-8636-d670d2654900'),
    o: { _id: ObjectId('6656da8b798ce72b3fdb83c3'), name: '백충덕', age: 99 },
    o2: { name: '백충덕', age: 99, _id: ObjectId('6656da8b798ce72b3fdb83c3') },
    stmtId: 0,
    ts: Timestamp({ t: 1716968075, i: 128 }),
    t: Long('4'),
    v: Long('2'),
    wall: ISODate('2024-05-29T07:34:35.599Z'),
    prevOpTime: { ts: Timestamp({ t: 0, i: 0 }), t: Long('-1') }
}
 
 
 
// insertMany
// _________________________________________________________________________
db.users.insertMany([
    {name: '조조', age: 10},
    {name: '관우', age: 20}
])
 
// oplog
{
    lsid: {
        id: UUID('e573bc34-6e6c-4d27-b858-083de253605e'),
        uid: Binary.createFromBase64('Y5mrDaxi8gv8RmdTsQ+1j7fmkr7JUsabhNmXAheU0fg=', 0)
    },
    txnNumber: Long('2'),
    op: 'i',
    ns: 'service.users',
    ui: UUID('a565958e-3734-4b67-8636-d670d2654900'),
    o: { _id: ObjectId('6656dace798ce72b3fdb83c4'), name: '조조', age: 10 },
    o2: { name: '조조', age: 10, _id: ObjectId('6656dace798ce72b3fdb83c4') },
    stmtId: 0,
    ts: Timestamp({ t: 1716968142, i: 8 }),
    t: Long('4'),
    v: Long('2'),
    wall: ISODate('2024-05-29T07:35:42.065Z'),
    prevOpTime: { ts: Timestamp({ t: 0, i: 0 }), t: Long('-1') }
},
{
    lsid: {
      id: UUID('e573bc34-6e6c-4d27-b858-083de253605e'),
      uid: Binary.createFromBase64('Y5mrDaxi8gv8RmdTsQ+1j7fmkr7JUsabhNmXAheU0fg=', 0)
    },
    txnNumber: Long('2'),
    op: 'i',
    ns: 'service.users',
    ui: UUID('a565958e-3734-4b67-8636-d670d2654900'),
    o: { _id: ObjectId('6656dace798ce72b3fdb83c5'), name: '관우', age: 20 },
    o2: { name: '관우', age: 20, _id: ObjectId('6656dace798ce72b3fdb83c5') },
    stmtId: 1,
    ts: Timestamp({ t: 1716968142, i: 9 }),
    t: Long('4'),
    v: Long('2'),
    wall: ISODate('2024-05-29T07:35:42.065Z'),
    prevOpTime: { ts: Timestamp({ t: 1716968142, i: 8 }), t: Long('4') }
}
 
 
 
 
// updateOne
// _________________________________________________________________________
db.users.updateOne({name:'백충덕'}, {$set:{lv: 1}})
 
// oplog
{
    lsid: {
      id: UUID('e573bc34-6e6c-4d27-b858-083de253605e'),
      uid: Binary.createFromBase64('Y5mrDaxi8gv8RmdTsQ+1j7fmkr7JUsabhNmXAheU0fg=', 0)
    },
    txnNumber: Long('4'),
    op: 'u',
    ns: 'service.users',
    ui: UUID('a565958e-3734-4b67-8636-d670d2654900'),
    // 존재하지 않던 필드가 추가되었으므로 o.diff.i
    o: { '$v': 2, diff: { i: { lv: 1 } } },
    o2: { name: '백충덕', age: 99, _id: ObjectId('6656da8b798ce72b3fdb83c3') },
    stmtId: 0,
    ts: Timestamp({ t: 1716968256, i: 35 }),
    t: Long('4'),
    v: Long('2'),
    wall: ISODate('2024-05-29T07:37:36.320Z'),
    prevOpTime: { ts: Timestamp({ t: 0, i: 0 }), t: Long('-1') }
}
 
db.users.updateOne({name:'백충덕'}, {$set:{lv: 2}})
 
// oplog
{
    lsid: {
      id: UUID('e573bc34-6e6c-4d27-b858-083de253605e'),
      uid: Binary.createFromBase64('Y5mrDaxi8gv8RmdTsQ+1j7fmkr7JUsabhNmXAheU0fg=', 0)
    },
    txnNumber: Long('5'),
    op: 'u',
    ns: 'service.users',
    ui: UUID('a565958e-3734-4b67-8636-d670d2654900'),
    // 존재하던 필드가 변경되었으므로 o.diff.u
    o: { '$v': 2, diff: { u: { lv: 2 } } },
    o2: { name: '백충덕', age: 99, _id: ObjectId('6656da8b798ce72b3fdb83c3') },
    stmtId: 0,
    ts: Timestamp({ t: 1716968391, i: 45 }),
    t: Long('4'),
    v: Long('2'),
    wall: ISODate('2024-05-29T07:39:51.388Z'),
    prevOpTime: { ts: Timestamp({ t: 0, i: 0 }), t: Long('-1') }
}
 
db.users.updateOne({name:'백충덕'}, {$inc:{lv: 1}})
 
// oplog
{
    lsid: {
      id: UUID('e573bc34-6e6c-4d27-b858-083de253605e'),
      uid: Binary.createFromBase64('Y5mrDaxi8gv8RmdTsQ+1j7fmkr7JUsabhNmXAheU0fg=', 0)
    },
    txnNumber: Long('9'),
    op: 'u',
    ns: 'service.users',
    ui: UUID('a565958e-3734-4b67-8636-d670d2654900'),
    // $inc 되었어도 o.diff.u 를 통해 변경되어야 하는 값으로 세팅됨 = 멱등성 보장 (idempotent) = 해당 oplog 몇번을 실행해도 결과는 동일
    o: { '$v': 2, diff: { u: { lv: 3 } } },
    o2: { name: '백충덕', age: 99, _id: ObjectId('6656da8b798ce72b3fdb83c3') },
    stmtId: 0,
    ts: Timestamp({ t: 1716968702, i: 15 }),
    t: Long('4'),
    v: Long('2'),
    wall: ISODate('2024-05-29T07:45:02.157Z'),
    prevOpTime: { ts: Timestamp({ t: 0, i: 0 }), t: Long('-1') }
}
 
db.users.updateOne({name: '백충덕'}, {$unset:{lv: ''}})
 
// oplog
{
    lsid: {
      id: UUID('e573bc34-6e6c-4d27-b858-083de253605e'),
      uid: Binary.createFromBase64('Y5mrDaxi8gv8RmdTsQ+1j7fmkr7JUsabhNmXAheU0fg=', 0)
    },
    txnNumber: Long('11'),
    op: 'u',
    ns: 'service.users',
    ui: UUID('a565958e-3734-4b67-8636-d670d2654900'),
    // 존재하던 필드가 사라졌으므로 o.diff.d
    o: { '$v': 2, diff: { d: { lv: false } } },
    o2: { name: '백충덕', age: 99, _id: ObjectId('6656da8b798ce72b3fdb83c3') },
    stmtId: 0,
    ts: Timestamp({ t: 1716968808, i: 69 }),
    t: Long('4'),
    v: Long('2'),
    wall: ISODate('2024-05-29T07:46:48.385Z'),
    prevOpTime: { ts: Timestamp({ t: 0, i: 0 }), t: Long('-1') }
}
 
 
 
 
// updateMany
// _________________________________________________________________________
db.users.updateMany({}, {$set:{lv:10}})
 
// oplog
// atomic을 보장하기 위해 조건에 해당하는 document당 1회씩 업데이트가 일어남
// 이런 이유로 명령실행수와 oplog수가 1:1이 아니게 되므로 보관가능한 oplog 범위가 예상과 크게 벗어날 가능성 있음
{
    op: 'u',
    ns: 'service.users',
    ui: UUID('a565958e-3734-4b67-8636-d670d2654900'),
    o: { '$v': 2, diff: { i: { lv: 10 } } },
    o2: { name: '백충덕', age: 99, _id: ObjectId('6656da8b798ce72b3fdb83c3') },
    ts: Timestamp({ t: 1716968853, i: 123 }),
    t: Long('4'),
    v: Long('2'),
    wall: ISODate('2024-05-29T07:47:33.644Z')
},
{
    op: 'u',
    ns: 'service.users',
    ui: UUID('a565958e-3734-4b67-8636-d670d2654900'),
    o: { '$v': 2, diff: { i: { lv: 10 } } },
    o2: { name: '조조', age: 10, _id: ObjectId('6656dace798ce72b3fdb83c4') },
    ts: Timestamp({ t: 1716968853, i: 124 }),
    t: Long('4'),
    v: Long('2'),
    wall: ISODate('2024-05-29T07:47:33.645Z')
},
{
    op: 'u',
    ns: 'service.users',
    ui: UUID('a565958e-3734-4b67-8636-d670d2654900'),
    o: { '$v': 2, diff: { i: { lv: 10 } } },
    o2: { name: '관우', age: 20, _id: ObjectId('6656dace798ce72b3fdb83c5') },
    ts: Timestamp({ t: 1716968853, i: 125 }),
    t: Long('4'),
    v: Long('2'),
    wall: ISODate('2024-05-29T07:47:33.645Z')
}
 
 
 
 
 
// deleteOne
// _________________________________________________________________________
db.users.deleteOne({name:'백충덕'})
 
// oplog
{
    lsid: {
      id: UUID('e573bc34-6e6c-4d27-b858-083de253605e'),
      uid: Binary.createFromBase64('Y5mrDaxi8gv8RmdTsQ+1j7fmkr7JUsabhNmXAheU0fg=', 0)
    },
    txnNumber: Long('18'),
    op: 'd',
    ns: 'service.users',
    ui: UUID('a565958e-3734-4b67-8636-d670d2654900'),
    o: { name: '백충덕', age: 99, _id: ObjectId('6656da8b798ce72b3fdb83c3') },
    stmtId: 0,
    ts: Timestamp({ t: 1716969662, i: 96 }),
    t: Long('4'),
    v: Long('2'),
    wall: ISODate('2024-05-29T08:01:02.565Z'),
    prevOpTime: { ts: Timestamp({ t: 0, i: 0 }), t: Long('-1') }
}
 
 
 
// deleteMany
// _________________________________________________________________________
db.users.deleteMany({lv:10})
 
// oplog (ns가 admin.$cmd 임에 주의)
{
    op: 'c',
    ns: 'admin.$cmd',
    o: {
      applyOps: [
        {
          op: 'd',
          ns: 'service.users',
          ui: UUID('a565958e-3734-4b67-8636-d670d2654900'),
          o: {
            name: '조조',
            age: 10,
            _id: ObjectId('6656dace798ce72b3fdb83c4')
          }
        },
        {
          op: 'd',
          ns: 'service.users',
          ui: UUID('a565958e-3734-4b67-8636-d670d2654900'),
          o: {
            name: '관우',
            age: 20,
            _id: ObjectId('6656dace798ce72b3fdb83c5')
          }
        }
      ]
    },
    ts: Timestamp({ t: 1716970514, i: 54 }),
    t: Long('4'),
    v: Long('2'),
    wall: ISODate('2024-05-29T08:15:14.514Z')
}

 

 

 

 

 

 

'DataBase' 카테고리의 다른 글

[MongoDB] chunk/range size 변경  (0) 2024.07.11
[MongoDB] chunk 관리  (0) 2024.07.04
[MongoDB] mongosh에서 JavaScript 사용하기  (0) 2024.02.27
[MongoDB] mongosh editor  (0) 2024.02.20
[MongoDB] .mongoshrc.js  (0) 2024.02.19
Posted by bloodguy
,