본문 바로가기

MongoDB

[MongoDB] 백업 및 복구

 

MongoDB 서버는 다인 레플리카 셋뿐만 아니라 샤딩된 클러스터 구성으로도 구축할 수 있다.

 

단일 레플리카 셋일 때는 다른 RDMBS와 동일하게 단일 서버의 백업만 고려하면 된다.

 

하지만 샤딩된 클러스터 구성일 때는 여러 샤드 간의 데이터 저장 시점을 동기화해야 하는 어려움이 있다.

 

그뿐만 아니라 컨피그 서버와 샤드 서버의 동기화 문제( 동일 시점 백업 )도

 

샤딩된 클러스터의 백업에 있어서 상당히 까다로운 문제일 것이다.

 

여기에서는 완벽한 백업 및 복구 방법은 아니지만, 가능한 몇 가지 방법을 살펴보겠다.

 

 


 

STEP#01. mongodump와 mongorestore를 이용한 놀리 백업 복구

 

#01. mongodump 백업

 

mongodump는 MongoDB에서 공식적으로 제공되는 유일한 백업 도구인데,

 

안타깝게도 mongodump는 논리적인 수준의 백업을 실행하는 도구다.

 

즉, mongodump는 MongoDB 서버의 데이터 파일을 물리적으로 복사하는 것이 아니라

 

MongoDB 서버에 로그인한 다음 도큐먼트를 한건 한건씩 덤프해서 BSON 파일로 저장하는 방식이다.

 

그래서 mongodump 도구는 백업 시간뿐만 아니라 복구 시간도 상당히 오래 걸린다.

 

긴급하게 데이터 파일을 복구해야 하는 상황에서 큰 데이터 파일을 모두 적재해서

 

복구해야 할 데이터를 가져온다는 것은 복구 시간을 상당히 지연시킬 것이다.

 

 

서비스 요건상 백업이나 복구 시간이 문제되지 않는다면 mongodump는 훌륭한 백업 도구가 될 것이다.

 

monogdump는 기본적으로 특정 시점의 스냅샷을 덤프하지는 않는다.

 

즉, monogdump 도구가 데이터를 덤프하는 동안 변경되는 데이터에 대해서는 백업이 일관된 상태를 유지하지 못한다.

 

컬렉션을 덤프하는 시점에 따라서 최근 변경이 포함될 수도 있지만 그렇지 못할 수도 있기때문이다

 

그래서 mongodump를 이용해서 백업하는 경우에는 —oplog 옵션을 이용해서

 

mongodump 명령을 실행해야만 덤프가 실행 중인 동안 변경되는 데이터 OpLog 이벤트를 같이 백업할 수 있다.

 

데이터를 복구시에 mongodump가 실행되는 동안 수집된 OpLog를 모두 재생해서 백업이 완료된 시점의 데이터베이스 상태를 만든다.

 

 

특정 시점의 일관된 상태를 백업하려면 --oplog 옵션을 사용해야 하지만, MongoDB 라우터( mongos )를 통해서 mongodump 백업을 수행하는 경우 --oplog 옵션을 사용할 수 없다.

 

그래서 --oplog 옵션으로 스냅샷 백업을 실행할 때는 MongoDB 서버에 직접 로그인해서 백업을 수행해야 하며,

 

다연히 해당 MongoDB 서버의 OpLog가 활성화돼 있어야만 스냅샷을 백업할 수 있다는 것도 기어해 두자.

 

다음 명령은 OpLog까지 동시에 백업하도록 mongodump를 실행하는 명령이다.

 

이때 "--username" 옵션으로 인해 인증을 위한 비밀번호를 확인하는 과정을 거치므로 "--password"옵션은 포함하지 않아도 된다.

 

혹시 다음 명령으로 실행했을 때 파라미터가 적절하지 않다고 에러 메시지가 발생하는 경우에는

 

“--password"옵션을 제거하고 실행하도록 하자.

 

 

$ mongodump --authenticationDatabase admin --username userid --password \ --oplog --out /data/backup/

 

 

기본적으로 mongodump는 4개의 쓰레드를 이용해서 데이터를 덤프한다.

 

그런데 서버의 CPU나 디스크 자원이 충분하다면 "--numParallelCollections" 옵션을

 

CPU 코어 개수의 1 ~ 2배 정도로 설정해서 mongodump를 실행하면 빠른 속도로 데이터를 덤프할 수 있다.

 

 

백업이 실행되면 mongodump는 "--out" 파라미터에 지정한 디렉터리로 덤프한 BSON 도큐먼트를 기록하고,

 

덤프가 실행되는 동안 OpLog 이벤트를 덤프해서 "oplog.bson"이라는 덤프 파일을 출력 디렉터리에 기록한다.

 

백업이 완료되면 다음과 같이 각 데이터베이스의 컬렉션이 디렉터리 단위로 저장되고,

 

OpLog의 내용이 덤프돼서 oplog.bson 파일로 저장된 것을 확인할 수 있다.

 

 

$ ls -alh /data/backup

 

 

#02. mongodump 백업

 

monogdump를 이용해서 백업된 데이터 파일을 복구하는 방법은 매우 간단하다.

 

"--oplogReplay" 옵션은 덤프된 데이터 파일을 모두 적재한 후에 백업 디렉터의 "oplog.bson" 파일을 적재하도록 해주는 옵션인데,

 

"oplog.bson" 파일은  "--oplog" 옵션을 사용해서 mongodump를 실행한 경우에만 생성되는 파일이다.

 

그래서 만약 mongodump로 백업하면서 "--oplog" 옵션 없이 백업을 실행했다면 "--oplogReplay"옵션을 사용하면 된다.

 

$ mongorestore --oplogReplay /data/backup

 

mongorestore 명령이 완료되면 백업된 시점의 데이터베이스 상태로 복구시켜준다.

 

물론 mongodump 명령은 local 데이터베이스 컬렉션은 별도로 백업하지 않기 때문에

 

local 데이터베이스는 원래 백업을 수행했던 MongoDB 서버와 다른 상태일 수도 있다.

 

하지만 local 데이터베이스는 사용자가 데이터를 저장하는 데이터베이스가 아니며,

 

MongoDB 서버가 시작되는데 있어서 아무런 문제가 없으니 무시해도 좋다.

 

 

Mongodump로 백업도니 데이터로부터 일부 데이터베이스나 컬렉션만 적재하고자 한다면

 

"--nsInclude"나 "--nsExclude" 옵션을 이용한다.

 

그리고 백업 디렉터리에서 특정 데이터베이스의 데이터를 다른 데이터베이스나 컬렉션으로 적재하고자 한다면

 

"--nsFrom"와 "--nsTo" 옵션을 활용하도록 하자.

 

또한, mongorestore 적재를 빠르게 실행하고자 한다면 "--numParallelCollections" 옵션을 활용하면 된다.

 

"--numParallelCollecions" 옵션의 기본값은 4인데, 이는 4개의 쓰레드를 이용해서 데이터를 적재한다.

 

만약 서버의 CPU나 디스크 가용량이 충분하다면 "--numParallelCollections" 옵션을

 

CPU 코어 개수의 1 ~ 2배 정도로 설정해서 적재하면 빠르게 데이터를 적재할 수 있다.

 

그런데 "--nameParallelCollections" 옵션을 2 이상으로 설정해서 데이터를 적재한다 하더라도

 

하나의 컬렉션이 매우 큰 경우에는 이 옵션은 적재 속도를 그다지 향상시키지 못할 수도 있다.

 

이런 경우에는 "--numInsertionWorkersPerCollection" 옵션을 적절히 조절하면 더 빠른 속도로 데이터를 적재할 수 있다.

 

 

 

 


 

STEP#02. 물리 백업 및 복구

 

MongoDB 서버에서 공식적으로 물리 백업이나 복구에 대한 도구는 제공하지 않고 있다.

 

하지만 조금 원시적인 방법을 활용하면 mongodump나 monorestore와 같은 논리적인 백업 및 복구 방식보다는

 

훨씬빠른 방법으로 데이터를 백업하고 복구할 수 있다.

 

물리적으로 데이터를 백업하는 방법으로는 여러가지 다른 방법을 생각할 수 있지만,

 

복구는 동일한 방법으로 수행하게 된다.

 

우선 물리 백업을 수행하는 몇 가지 방법을 살펴보자.

 

 

#01. 셧다운 상태의 백업

 

mongodump 도구는 데이터베이스의 모든 컬렉션 도큐먼트를 읽어서 덤프하므로

 

MongoDB 서버의 부하를 유발함과 동시에 상당히 오랜 시간이 필요하다.

 

그래서 데이터가 큰 경우에는 덤프 시간이 하루이틀을 넘어서 버릴 수도 있다.

 

그래서 이를 막기 위해서 물리적인 백업 방법을 고려해야 하는데, 가장 간단한 방법은 서비스에 투입되지 않은

 

세컨드리 멤버의 MongoDB 서버를 셧다운하고 데이터 파일을 복사하는 방법이다.

 

## MongoDB 서버 종료
$ systemctl stop mongod.service

## MongoDB 서버의 데이터 디렉터리를 백업 디렉터리로 복사
$ cp -r /mongodb_data/data/backup/

 

 

매우 간단한 방법이지만, 이렇게 세컨드리 멤버를 셧다운 하는 경우에는 MongoDB 레플리카 셋의 고가용성을 고려해야 한다.

 

즉, 백업을 위해서 세컨드리 멤버가 셧다운 된 상태이므로 백업을 실행하는 동안에는 다른 멤버에서 장애가 발생했을 때

 

새로운 프라이머리 멤버가 선출되지 못하면서 서비스 할 수 없는 상태가 될 수 있다는 점이다.

 

그래서 만약 셧다운 한 후에 백업을 수행하고자 한다면 백업 전용의 새로운 멤버를 추가해서 진행할 것을 권장한다.

 

 

#02. 복제 중지 상태의 백업

 

MongoDB 레플리카 셋에서 세컨드리 멤버는 복제를 통해서만 데이터가 변경된다.

 

그래서 세컨드리 멤버의 복제 동기화를 멈추면 해당 세컨드리 멤버의 데이터 변경은 발생하지 않는다.

 

안타깝게도 MognoDB 서버에서는 공식적으로 복제를 멈추는 명령이 없다.

 

그래서 다음과 같이 db.fsyncLock() 명령을 이용해서 데이터 파일을 동기화하고,

 

글로벌 잠금을 거는 방법을 복제 동기화를 멈추는 방법으로 사용할 수 있다.

 

 

## 글로벌 잠금 획득( 복제 쓰기 멈춤 )
mongo> db.fsyncLock( { fsync : 1, lock : true } );

## MongoDB 서버의 데이터 디렉터리를 백업 디렉터리로 복사
$ cp -r /mongodb_data /data/backup/

## 데이터 디렉터리 복사가 완료되면 글로벌 자금 해제
mongo> db.fsyncUnlock();

 

db.fsyncLock() 명령을 사용하는 경우에는 데이터 디렉터리의 복사가 완료될 때까지

 

db.fsyncLock() 명령을 실행했던 컨넥션을 닫지 않고 유지해야 한다는 것을 기억해야 한다.

 

그리고 데이터 파일 복사가 완료되면 db.fsyncLock()을 실행했던 컨넥션에서

 

db.fsyncUnLock() 명령을 실행해서 글로벌 잠금을 해제하는 것도 잊지 않도록 하자.

 

 

db.fsyncLock() 명령을 이용하는 경우에도 「 #01. 셧다운 상태의 백업 」 방법과 같이

 

MongoDB 레플리카 셋의 고가용성에 영향을 미친다.

 

우선 db.fsyncLock() 명령이 실행 중인 상태에서도 레플리카 셋의 멤버들 간의 하트비트( Heartbeat )나

 

새로운 프라이머리 선출에 참여할 수는 있다.

 

하지만 이 실행되는 동안 지연된 복제로 인해서 새로운 프라이머리 멤버로 선출되지 못하거나

 

복제 동기화를 위해서 매우 오랜 시간이 소요될 수도 있다는 것에 주의하자.

 

 

#03. 파일 시스템 스냅샷

 

리눅스의 LVM과 같이 파일 시스템 레벨에서 지원하는 스냅샷 기능을 이용해서 물리 백업을 수행할 수도 있다.

 

물론 LVM뿐만 아니라 아마존 AWS의 EBS( Elastic Block Store ) 볼륨 백업 기능도 동일한 방법으로 상요할 수 있을 것이다.

 

LVM이나 아마존 EBS 볼륨 스냅샷 백업은 리눅스나 아마존 AWS의 메뉴얼로 참조하자.

 

 

#04. Percona 온라인 백업

 

MongoDB 서버를 셧다운 하거나 db.fsyncLock()을 사용하는 백업 방법을 MongoDB 레플리카 셋의 고가용성에 영향을 미친다.

 

또한, 파일 시스템 스냅샷 백업은 별도의 파일 시스템 소프트웨어를 설치해야만 사용할 수 있다.

 

그래서 Percona에서는 이런 번거로움과 어려움을 해결하기 위해서 MongoDB 서버에 온라인 백업 기능을 구현했다.

 

그래서 Parcona에서 구현한 온라인 백업을 사용하려면  Percona에서 배포하는 "Percona Server for MongoDB" 배포판을 사용해야 한다.

 

mongo> use admin
mongo> db.runCommand( { createBackup : 1, backupDir : "/data/backup" } );
{ "ok" : 1 }

 

백업이 정상적으로 완료되면 결과로 "{ ok : 1 }"이 반환되며, 지정된 디렉터리에 데이터 베이스의 컬렉션들이 백업된 것을 확인 할 수 있다.

 

Percona MongoDB 서버가 구현한 온라인 백업 기능은 WiredTiger 스토리지 엔진의 체크포인트 기능을 활용한 것인데,

 

WiredTiger 스토리지 엔진에서 백업용 체크포인트를 실행하고 데이터 파일의 복사가 완료될 때까지 백업 체크포인트를 종료하지 않는 형태로 구현한 것이다.

 

그래서 백업해야 할 데이터 디렉터리가 큰 경우에는 파일 복사 시간이 오래 걸리고,

 

파일 복사 시간 동안 데이터 변경이 많아지면 ( 최악의 경우 )각 컬렉션의 데이터 파일 크기가 몇 배로 커질 수도 있다.

 

그런데 문제는 이렇게 한번 커져 버린 데이터 파일은 컴팩션이나 컬렉션 리빌드를 수행하지 않고서는 다시 원래대로 크기를 줄일 수 없다는 것이다.

 

 

만약 데이터 파일이 매우 크고 데이터 변경도 많다면

 

Percona MongoDB의 온라인 백업 기능을 먼저 테스트해보고 서비스용 MongoDB 서버에 적용하도록 하자.

 

 

#05. 물리 백업 복구

 

물리적으로 백업된 데이터 파일은 운영 중인 MongoDB 서버의 데이터 디렉터리 구조와 컬렉션의 데이터 파일을 그대로 가지고 있기 때문에 복구가 매우 간단하다.

 

복구하고자 하는 MongoDB 서버의 데이터 디렉터리에 백업된 데이터 파일을 그대로 복사하기만 하면 된다.

 

물론 백업된 MongoDB 서버가 가지고 있던 설정 파일에서 데이터 파일의 구조나 내용에

 

연관된 옵션( 데이터 파일의 페이지 크기와 압축 여부 등 )은 모두 동일하게 해줄 것을 권장한다.

 

 

만약 샤딩된 MongoDB 클러스터에서 백업된 데이터 파일을 이용해서 복구하는 경우에는

 

백업된 데이터 파일을 사용해서 시작되는 MongoDB 서버가 아무런 응답도 없이 무한정 대기 상태로 빠지는 경우도 있는데,

 

이는 백업을 실행했던 원본 MongoDB 서버가 소속된 클러스터의 구조를 확인하기 위해서 컨피그 서버로 접속하려고 하기 때문이다.

 

그런데 만약 기존 컨피그 서버가 이미 존재하지 않는 상태라면 무한정 대기 상태로 빠져버리게 된디ㅏ.

 

그래서 기존 샤드 클러스터와 무관하게 백업된 데이터 파일을 이용해서 새로운 MongoDB 서버를 시작하거나

 

기존 컨피그 서버가 없어진 경우에는 MongoDB 서버의 시작 옵션에서 다음과 같이 recoverShardingState 옵션을 false로 설정하면 기존 샤드 클러스터의 컨피그 서버 정보를 무시하고 MongoDB 서버를 시작하게 된다.

 

recoverShardingState 옵션은 MongoDB 3.4에서만 사용 가능하며, MongoDB 3.6 버전에서는 MongoDB 서버 시작시 레플리케이션 관련 옵션을 모두 제거하고 실행하면,

 

MongoDB 3.4버전에서 recoverShardingState를false로 설정한 것과 동일한 효과를 얻을 수 있다.

 

## MongoDB 서버 시작 시 옵션 사용
$ mongod --setParameter=recoverShardingState=false -f mongod.conf

## MongoDB 설정 파일에 옵션 적용
...
setParameter :
  recoverShardingState : false

 

 


 

 

STEP#03. PIT( Point - In - Time ) 복구

 

일반적으로 RDBMS는 단일 인스턴스로 구축되는 경우가 많은데,

 

이렇게 단일 인스턴스로 구성되는 경우에 서버가 이상 증상을 보이거나

 

하드웨어 폴트( Fault )가 발생하면 모든 데이터를 잃게 될 가능성이 높다. 

 

그렇게 이렇게 데이터가 손실된 경우 백업된 데이터를 이용해서 복구하게 된다.

 

하지만 MongoDB 서버는 일반적으로 레플리카 셋으로 구축하기 때문에 운영 체제나 하드웨어 폴트로 인해서 데이터가 손실된다 하더라도

 

레플리카 셋이 새로운 프라이머리로 대체되므로 백업이 없어도 복구할 수 있는 경우가 많다.

 

하지만 MongoDB 서버에서도 사용자나 운영자의 실수로 데이터가 삭제되는 경우에 복구를 하려면 백업된 데이터가 필수이다.

 

 

이렇게 사용자나 운영자의 실수로 데이터가 삭제된 경우에는 백업을 이용해서 데이터가 삭제되기 전까지의 데이터 복구가 필수라고 볼 수 있다.

 

이렇게 잘못 실행된 명령 직전까지의 데이터를 복구하는 것을 PIT( Point-IN-Time )보구라고 한다.

 

MongoDB에서 PIT 복구는 물리( 데이터 파일 복사 ) 또는 논리( MongoDB 덤프 ) 풀 백업에 최근의 OpLog를 복구하는 방식으로 진행된다.

 

우선 PIT 복구를 수행하는 일반적인 과정을 간단히 살펴보자.

 

① 물리 백업 복구
② 복구된 백업 시점부터 가장 최근 시점까지의 OpLog 백업( 가용한 레플리카 셋 멤버로부터 )
③ 실수로 실행된 명령의 OpLog 이벤트의 타임스탬프 위치 확인
④ 물리 백업 복구된 서버에 백업 시점부터 실수로 실행된 명령의 직전까지의 OpLog 적용

 

위 절차의 2번 OpLog 백업은 레플리카 셋 중에서 마지막 백업( 1번에서 복구한 시점 ) 시점 부터의 OpLog를 가진 멤버가 있어야만 가능하다.

 

만약 마지막 백업이 일주일 전에 실행됐는데, 레플리카셋이 보관 중인 OpLog가 최근 3일뿐이라면 PIT 복구는 불가능하다.

 

그래서 가능하면 OpLog의 용량을 크게 해서 OpLog의 보관 주기를 길게 해두면 데이터 복구의 가능성이나 백업 주기를 길게 설정할 수 있다.

 

 

우선 복구한 백업의 OpLog 시점을 확인하기 위해서 백업을 복구한 MongoDB 서버에 로그인해서

 

local 데이터베이스의 oplog.rs 컬렉션의 가장 마지막 이벤트를 확인해 보자.

 

OpLog의 이벤트별로 시점은 "ts" 필드에 저장된 Timestamp 값을 기준으로 한다.

 

다음 명령은 oplog.rs 컬렉션의 가장 최근 이벤트 하나를 조회해서 "ts" 필드의 값을 출력해보는 것이다.

 

이 예제에서는 oplog.rs 컬렉션의 마지막 이벤트가 "Timestamp( 1459850401, 11 )"라는 것을 알 수 있다.

 

예제의 명령은 "ts" 필드를 역순으로 정렬해서 한 건만 조회하고 있는데, 실제 이 쿼리는 "ts" 피드로 정렬을 해야 하므로 상당히 느리게 실행될 것이다.

 

그런데 oplog.rs 컬렉션은 Capped 컬렉션이므로 컬렉션을 역순으로 정렬해서 한 건만 가져와도 같은 결과를 얻을 수 있다.

 

mongo> db.oplog.rs.find( {}, { ts : 1, } ).sort( { ts : -1 } ).limit( 1 );
{ "ts" : Timestamp( 1459850401, 11 ) }

mongo> db.oplog.rs.find( {}, { ts : 1, } ).sort( { $natural : -1 } ).limit( 1 );
{ "ts" : Timestamp( 1459850401, 11 ) }

 

이제 가장 최근까지의 OpLog를 가진 다른 MongoDB 서버( 동일 레플리카 셋에 포함된 MongoDB 서버 )에서

 

OpLog 이벤트 "Timestamp( 1459850401, 11 )"부터 덤프하도록 한다.

 

물론 MongoDB의 OpLog는 멱등( Idempotent ) 특성이 있기 때문에

 

"Timestamp( 1459850401, 11 )" 이벤트 이전부터 덤프해도 무관하다.

 

$ mongodump --db local --collection oplog.rs\
    --query '{ "ts" : { "$gt" : { "$timestamp" : { "t" : 1459850401, "i" : 11 } } } }' \
    --out backup_oplog

 

이제 덤프된 OpLog에서 실수로 데이터를 삭제한 이벤트의 시점을 찾아야 하는데,

 

덤프된 OpLog는 BSON 파일이므로 사람의 눈으로 확인할 수가 없다.

 

그래서 우선 OpLog 덤프 파일을 JSON 파일로 변환하도록 하자.

 

$ bsondump backup_oplog/local/oplog.rsbson > oplog.json

 

이제 oplog.json 파일을 텍스트 편집기를 이용해서 실수로 실행된 삭제 명령의 위치( OpLog 이벤트의 "ts" 필드 값 )을 검색해보자.

 

물론 덤프된 OpLog 파일이 클 때는 "grep"과 같은 운영 체제의 유틸리티를 이용해서 이벤트를 검색할 수도 있다.

 

그런데 실수로 삭제를 실행한 명령의 위치를 찾는 것은 그렇게 쉬운 작업이 아니다.

 

우선 MongoDB 서버의 OpLog가 가지는 2가지 특성을 이해해야 한다.

 

 

  • OpLog는 명령 단위로 묶여서 기록되지 않는다.
  • OpLog는 사용자가 실행한 명령을 로깅하는 것이 아니라 변경된 데이터의 결과를 저장한다.

 

예를 들어, 사용자가 다음과 같은 명령을 실행했다고 가정해보자.

 

mongo> use mysns
mongo> db.users.remove( { name : "matt" } );

 

"name" 필드가 "matt"인 사용자를 삭제했을 때, MongoDB 서버의 OpLog는 이 삭제 명령을 기록하는 것이 아니라

 

삭제 명령으로 인해서 삭제된 도큐먼트의 프라이머리 키( "_id" )를 기록한다.

 

그리고 만약 "name" 필드가 "matt"인 사용자가 10건이 있었다면 MongoDB의 OpLog는 한 건이 아니라 10건의 도큐먼트를 기록하게 된다.

 

그래서 삭제 명령에 사용했던 조건 ( "{ name : " matt" } " )으로 OpLog 이벤트를 검색해도 아무런 결과가 보이지 않으므로

 

사용자가 삭제 명령을 실행한 이벤트의 위치를 찾기는 쉽지 않다.

 

그런데 한 가지 더 삭제 지점의 OpLog 이벤트를 찾기 어렵게 만드는 것은 트랜잭션이 없다는 것이다.

 

예를 들어. "name" 필드가 "matt"인 도큐먼트가 10건이라면 MongoDB는 10건의 OpLog건의 OpLog이벤트를 기록할 것이다.

 

그런데 10건의 도큐먼트가 삭제되는 동안 다른 데이터변경이 실행됐다면

 

10건의 도큐먼트 삭제와 다른 컨넥션의 데이터 변경이 순서가 섞여서 OpLog에 기록된다.

 

그래서 단순히 "grep"과 같은 문자열 검색 유틸리티로는 문제의 OpLog 이벤트를 검색하는 것이 불가능할 수도 있다.

 

이는 직접 눈으로 확인하면서 수동으로 찾아야 하는 작업인데, 예를 들어 다음과 같이 삭제된 이벤트를 찾았다고 가정해 보자.

 

monbo> db.oplog.rs.find().sort( { $natural : -1 } ).limit( 100 ).pretty();
...
{
	  "ts" : Timestamp( 1459852199, 1 )
    , "t" : NumberLong( 1 )
    , "h" : NumberLong( "-5720026190256829571" )
    , "v": 2
    , "op" : "d"
    , "ns" : "mysns.users"
    , "o" : {
    	"_id" : ObjectId( "59d8b826204c044d631b78de" )
    }
}
...

이제 OpLog 이벤트를 재생해야 할 위치를 모두 찾았다.

 

OpLog의 재생 시작 지점은 "Timestamp( 1459850401, 11 )"이며 재생을 멈춰야 할 시점은 "Timestamp( 1459852199, 1 )" 직전까지다.

 

"Timestamp( 1459852199, 1 )" 이벤트 실수로 삭제된 명령이므로 이 명령은 재생되면 안 된다는 것에 주의하자.

 

OpLog의 재생을 위해서 mongorestore 명령을 사용하는데, 이때 백업 데이터 파일이 저장된 디렉터리( backup_oplog 디렉티러 )에는

 

덤프 받은 oplog.bson 파일만 있어야 한다.

 

그렇지 않고 다른 데이터베이스의 컬렉션 덤프 파일이 있으면 이 데이터 파일들도 기존 컬렉션에 다시 적재돼서 데이터가 이중으로 적재될 수도 있다.

 

또한, mongorestore 명령으로 OpLog를 재실행할 때에는 "--oplogReplay" 옵션을 사용해야 하며,

 

덤프된 OpLog에서 재생을 멈춰야 할 OpLog 이벤트 지점( "Timestamp( 1459852199, 1 )" )을 "--oplogLimit" 옵션에 같이 지정해야 한다.

 

"--oplogLimit"옵션에는 다음 예제처럼 "타임스탬프 : 시퀀스" 형식으로 타임스탬프 지점을 지정하면

 

해당 이벤트 지점 직전까지 OpLog를 재생하고 mongorestore 명령이 멈춘다.

 

## mongorestore 명령이 OpLog 덤프 파일을 인식할 수 있도록 디렉터리 경로와 파일명 변경
$ mv backup_oplog/local/oplog.rs.bson backup_oplog/oplog.bson

// mongorestore 명령을 이용해서 OpLog 이벤트 재생
$ mongorestore --oplogReplay --oplogLimit 1459852199:1 backup_oplog

 

지금까지의 과정을 거쳐서 삭제 명령이 실행된 지점( "Timestamp( 1459852199, 1 )" ) 직전까지 데이터의 복구가 완료됐다.

 

이제 삭제된 데이터를 덤프 받아서 서비스용 MongoDB 서버의 데이터를 복구하거나

 

복구된 MongoDB 서버를 레플리카 셋으로 투입해주면 도큐먼트가 삭제되기 직전의 상태로 서비스 할 수 있다.

 

 


 

※ 주의

 

MongoDB에서 데이터 변경 명령은 트랜잭션이 지원되지 않기 때문에 다른 컨넥션의 데이터 변경 명령과 삭제 명령의 OpLog가 순서가 섞여서 oplog.rs 컬렉션에 기록될 수 있다.

 

예를 들어, 다음과 같이 실수로 실행된 REMOVE 명령( 컨넥션 1번 )으로 인한 도큐먼트 변경과 다른 컨넥션의 도큐먼트 변경 이벤트가 섞여서 OpLog에 기록될 수 있다.

 

Timestamp( 1459852199, 5 ) → 컨넥션 1번의 REMOVE로 인한 도큐먼트 변경
Timestamp( 1459852199, 4 ) → 컨넥션 3번의 INSERT로 인한 도큐먼트 변경
Timestamp( 1459852199, 3 ) → 컨넥션 1번의 REMOVE로 인한 도큐먼트 변경
Timestamp( 1459852199, 2 ) → 컨넥션 2번의 UPDATE로 인한 도큐먼트 변경
Timestamp( 1459852199, 1 ) → 컨넥션 1번의 REMOVE로 인한 도큐먼트 변경

 

이렇게 OpLog 이벤트가 섞여 있을때는 실수로 실행된 REMOVE 명령의 첫 번째 변경 직전까지만 mongorestore 명령으로 PIT 복구를 실행하고,

 

그 이후는 oplog.json 파일을 참조하면서 수동으로 명령을 만들어서 직접 하나씩 복구를 수행하는 것이 좋다.

 

mongorestore 명령은 OpLog 덤프 파일의 어디까지 실행할 것인지 옵션을 설정할 수는 있지만,

 

어디서부터 재실행을 시작할지 설정할 수 있는 기능의 옵션이 없다.

 

그래서 중간에 띄엄띄엄 OpLog를 재생하려면 OpLog 덤프를 매번 실행해야 하므로 매우 번거롭고 오랜 시간이 걸리는 작업이 될 것이다.