11. 복제 셋 구성요소

11.1 동기화

  • 로그, 즉 oplog를 보관함으로써 복제를 수행한다.

Replica Set Oplog 네이밍이다.

Oplog는 Replica set의 데이터를 동기화를 위해 내부에서 발생하는 모든 동작의 로그를 기록한 것 이다.

  • 세컨더리 다운되면 재시작할 때 oplog에 있는 마지막 연산과 동기화한다.
  • oplog 작업은 멱등이다. (동일한 데이터 결과셋)
  • 크기가 고정되어 있어서 연산의 수가 정해져있다.
    • 기본크기로 괜찮다.
  • 다만, 삭제만 oplog 여러개 항목으로 분해된다.
    • 예를 들어서 db.col.remove() 로 도큐먼트 100만개를 삭제하면 oplog 항목 100만개를 하나씩 삭제한다.

아래의 케이스의 경우 기본 oplog크기보다 더 크게 줘야하는 케이스가 된다.

한 번에 여러 도큐먼트 갱신

  • oplog의 멱등성을 유지하기 위해 다중갱신을 개별 작업으로 변환해야 한다.
    • 각의 작업이 독립적이며 그 자체로 완전한 결과를 낼 수 있도록 설계되어야 하고, 그 결과가 여러 번 수행되어도 변경되지 않아야 한다는 것을 의미

삽입한 데이터와 동일한 양의 데이터 삭제

  • 삭제에서 디스크 사용량 측면은 크게 증가하지 않지만 oplog 크기는 클 수 있다.

상당한 수의 내부 갱신

  • 크기를 증가시키지 않는 갱신이라면 데이터베이스에 기록되는 작업의 수는 많지만 디스크 데이터 양은 변하지 않는다.

이러한 크기 조정은 mongod가 oplog를 만들기 전에 oplogSizeMB 옵션을 사용해 크기를 지정할 수 있다.

https://oreil.ly/mh5SX 참고 하면 좋겠다. 🙂

그러면 동기화는 어떻게 할까?

11.1.1 초기 동기화

  • 복제 셋의 한 멤버에서 다른 멤버로 모든 데이터를 복사한다.
    • 다만, 유효한 상태인지 확인한다.
    • 만약, 유효하다면 복제셋의 다른 멤버의 데이터 전체를 복사한다.
  • 이러한 프로세 단계는 mongod 로그에서 확인할 수 있다.
2023-10-01T12:00:00.123+0000 I REPL     [replication-0] Starting initial sync with source: replicaSetA:27017
2023-10-01T12:00:05.456+0000 I REPL     [replication-0] Initial sync data cloning for database 'testDb' started
2023-10-01T12:05:00.789+0000 I REPL     [replication-0] Initial sync cloning of collection 'testDb.collection1' finished
2023-10-01T12:10:00.012+0000 I REPL     [replication-0] Initial sync data cloning completed
2023-10-01T12:15:00.345+0000 I REPL     [replication-0] Data consistency checks are starting
2023-10-01T12:20:00.678+0000 I REPL     [replication-0] Data is consistent
2023-10-01T12:25:00.901+0000 I REPL     [replication-0] Initial sync applying oplog entries
2023-10-01T12:30:00.234+0000 I REPL     [replication-0] Initial sync apply phase finished
2023-10-01T12:35:00.567+0000 I REPL     [replication-0] Initial sync completed successfully //초기 동기화 완료

→ 그러면, 복제는 어떤것을 하냐면?

  • local 데이터베이스를 제외한 모든 데이터베이스를 복제한다.
  • mongod는 각 소스 데이터베이스 내 컬렉션을 모두 스캔해서 복사하려고 하는 멤버의 컬렉션의 복사본에 삽입하게 된다.

복제를 할 때 세컨더리가 프라이머리에 있는 데이터셋과 일치해야 하는데

  • 복제셋은 하나의 프라이머리 노드 하나 이상의 세컨더리 노드로 구성 (쓰기와 읽기가 구분)
  • 데이터의 모든 변경사항은 oplog에 기록
  • 세컨더리 노드는 자신의 로컬 oplog를 주기적으로 스캔하여, 프라이머리 노드의 최신 변경사항을 파악 (컬렉션들에 의해 복제, 데이터 일관성 보장)
  • 세컨더리 노드는 프라이머리 노드에서 받은 oplog 항목을 자신의 데이터베이스에 적용

세컨더리 노드가 프라이머리 노드의 데이터셋과 일치하도록 만드는 데 필수

  1. 초기 동기화 과정을 완료
    1. 세컨더리 노드가 새로 복제 셋에 추가되면, 초기 동기화 과정이 시작됩니다. 이 과정에서 세컨더리 노드는 프라이머리 노드로부터 모든 데이터를 처음부터 복제
    2. 세컨더리 노드는 프라이머리 노드의 모든 데이터베이스와 컬렉션을 복사
    3. 데이터 복사가 완료된 후, 세컨더리 노드는 프라이머리 노드의 oplog(운영 로그)를 복사하여 시작 시점부터 현재까지의 모든 변경사항을 적용
    4. oplog의 모든 항목이 세컨더리 노드에 적용되면, 초기 동기화 과정이 완료
  2. 일반 동기화 단계로 전환
    1. 초기 동기화가 완료된 후, 세컨더리 노드는 프라이머리 노드의 oplog를 지속적으로 모니터링하고 복제
    2. 세컨더리 노드는 프라이머리 노드의 oplog를 주기적으로 확인하고, 새로운 업데이트가 있을 때마다 이를 적용하여 데이터의 일관성을 유지
    3. 만약 프라이머리 노드에 문제가 발생하여 사용할 수 없게 되면, 복제 셋의 세컨더리 노드 중 하나가 새로운 프라이머리 노드로 승격될

과정을 거치게 된다.

oplog 
프라이머리 기록
세컨더리 동기화 안된 상태는 

이러한 과정에서 데이터의 신뢰성과 가용성을 높힌다.

이러한 복제를 수행할 때 작업 셋을 망칠 수 있는데,

  • 초기 동기화 과정에서 자주 사용하는 데이터는 추출하여 메모리로 페이징되는 과정에서 멤버가 급격하게 느려지게 된다.
    • 램에서 디스크로 향하기 때문에
      • 복사된 데이터는 처리를 위해 세컨더리 노드의 RAM(랜덤 액세스 메모리)에 로드됩니다. 자주 사용되는 데이터가 많은 경우, 메모리 사용량이 급격히 증가
      • 메모리가 포화 상태에 이르면, 운영 체제는 메모리에 있는 일부 데이터를 디스크로 스왑(페이징) → 이때 램에서 디스크로 (디스크의 입출력 속도가 메모리의 속도보다 훨씬 느리기 때문에 시스템의 반응 속도가 저하)
      • 페이징으로 인해 디스크 I/O가 증가하고, 이는 전체 시스템의 성능 저하로 이어진다.
      • 결과적으로는… 세컨더리 노드는 데이터 처리 속도가 느려지며, 초기 동기화에 필요한 시간이 길어진다.
  • 그리고 시간이 오래걸린다.

11.1.2. 복제

  • 세컨더리 멤버는 초기 동기화 후 지속적으로 데이터를 복제한다. (비동기로)

11.1.3. 실효처리

  • 세컨더리가 만약 동기화 진행이 미흡하게 되면 실효상태가 된다.
    • 세컨더리 노드가 프라이머리 노드와의 동기화를 유지하지 못하게 되어 최신 데이터의 상태를 반영하지 못하는 상황 ⇒ 데이터 일관성과 무결성에 영향을 줄 수 있음
  • 아래 케이스에서 실효상태가 발생할 수 있다.
    • 네트워크 장애, 유지보수로 세컨더리 노드의 다운타임
      • 세컨더리 노드가 다운되어 있는 동안, 프라이머리 노드에서는 계속 데이터 변경이 일어나고 이 변경사항들이 oplog에 기록
      • 세컨더리 노드가 다시 온라인이 되었을 때, 동기화해야 할 데이터 양이 너무 많아져서, oplog window(세컨더리가 복구할 수 있는 oplog의 시간 범위)를 초과할 수 있으며, 이로 인해 세컨더리가 최신 상태로 복구될 수 없음
    • 프라이머리 노드의 과도한 쓰기 요청
      • 과도한 쓰기 작업으로 인해 생성되는 데이터 변경 로그(oplog)의 양이 많아짐
      • 세컨더리 노드는 이 모든 변경을 실시간으로 동기화하기 어려워질 수 있으며, 필요한 데이터를 놓치거나 동기화가 미흡
  • 실효상태가 발생하는 원인 중 하나에는 아래 케이스로 동기화를 계속 진행하게 될 경우 일부 작업을 건너뛰게 된다. 이래서 발생하는듯?
    • 세컨더리가 다운타임, 쓰기 요청이 많거나 이런 경우가 된다.

11.2 하트비트

  • 멤버는 복제 셋에 대한 최신 정보를 유지하기 위해 복제 셋의 모든 멤버로 2초마다 하트비트 요청을 보낸다.
  • 과반수 도달 여부로 결정하는듯?
    • 결정할 수 없다면 스스로 세컨더리가 된다.

예시

  • 복제 셋 구성: 복제 셋이 5개의 멤버(3개의 세컨더리와 1개의 프라이머리, 1개의 아비터)로 구성되어 있다고 가정
  • 하트비트 발송: 각 멤버는 다른 모든 멤버에게 주기적으로 하트비트를 보내고. 이 신호는 멤버가 살아있고 네트워크에 연결되어 있음을 확인
  • 프라이머리의 장애: 프라이머리 노드가 네트워크 장애로 인해 다른 멤버와의 연결이 끊어지면, 나머지 멤버들은 프라이머리 노드로부터 하트비트를 수신하지 못함
  • 과반수 확인: 나머지 멤버들은 프라이머리 노드를 포함한 다른 멤버들로부터 과반수 이상의 하트비트 응답을 받지 못하면, 새로운 프라이머리 노드 선출을 위한 선거(election) 과정을 시작
  • 선거 과정: 선거는 살아있는 멤버들 중에서 새로운 프라이머리 노드를 선출하기 위해 진행. 과반수 이상의 멤버들이 하나의 멤버에게 투표해야 새로운 프라이머리가 선출
  • 과반수 도달 실패: 만약 네트워크 분할(network partition)로 인해 어떠한 그룹도 과반수에 도달하지 못한다면, 그 그룹의 멤버들은 스스로를 세컨더리 상태로 전환이 상태에서는 새로운 데이터의 쓰기가 불가능하며, 클러스터의 데이터 일관성을 유지하기 위해 읽기 전용 상태로 남음
  • 복구 및 재연결: 네트워크 상태가 복구되고 멤버들이 다시 연결되면, 하트비트를 통해 멤버들의 상태를 재확인하고 필요하다면 새로운 프라이머리 선출 과정이 다시 시작

11.2.1 멤버 상태

  • 멤버들은 하트비트를 통해 상태를 주고 받는다.

상태에는 다음의 것들이 있다.

  • startup
    • MongoDB 서버 인스턴스가 방금 시작되었고, 아직 구성을 완료하지 않은 상태
    • 서버를 처음 시작할 때, 데이터베이스는 자동으로 이 상태로 설정되며, 기본 구성 및 초기화 작업을 수행
  • startup2
    • 데이터베이스는 초기화를 마쳤으나 아직 복제 셋의 다른 멤버들과 통신을 시작하지 않은 상태
    • 초기 설정 단계에서 인덱스 구축, 기존 데이터 검사 등을 완료한 후 복제 프로세스 준비 단계
  • recovering
    • 이 멤버는 현재 데이터 복구 모드에 있으며, 동기화 중이거나 장애로 인해 복구 중인 상태
    • 복제 셋에서 하나의 노드가 장애 후 복구 중이거나, 초기 동기화 중 발생한 문제를 해결 중일 때 이 상태를 가질 수 있음
  • arbiter
    • 아비터는 복제 셋에서 투표만을 수행하는 특수한 유형의 멤버로, 데이터를 저장하지 않
    • 복제 셋의 멤버 수를 홀수로 유지하기 위해 아비터를 추가하여, 선거 프로세스에서 투표 권한을 갖음
  • down
    • 이 상태는 멤버가 네트워크에 연결되지 않거나 작동하지 않는 상태를 나타냄
    • 서버 하드웨어 실패, 네트워크 문제 등으로 인해 멤버에 접근할 수 없을 때 이 상태
  • unknown
    • 멤버의 현재 상태를 다른 멤버들이 파악할 수 없는 경우
    • 네트워크 분할로 인해 멤버 간 통신이 끊어진 경우, 다른 멤버들은 해당 멤버의 상태를 'unknown'으로 간주
  • removed
    • 이 멤버는 복제 셋에서 제거되었으나 아직 구성에서 완전히 삭제되지 않은 상태
    • 복제 셋에서 멤버를 수동으로 제거한 후, 구성에서 이 멤버를 청소하는 작업이 진행
  • rollback
    • 데이터의 일관성을 유지하기 위해, 이 멤버는 데이터를 이전 상태로 되돌리는 중.
    • 세컨더리 노드가 프라이머리 노드와의 동기화 과정에서 충돌이 발생했을 때, 불일치하는 데이터를 롤백하여 일관성을 회복

11.3 선출

  • 멤버가 프라이머리에 도달하지 못하면 프라이머리 선출을 모색한다.
  • 그리고 자격도 확인해본다. (복제에서 뒤쳐질 수 있다.)
    • 데이터 동기화 상태
    • 네트워크 연결 상태
    • 투표권한
    • oplog윈도우
  • 프라이머리가 되지 못한 즉, 과반수의 동의를 얻지못한 멤버는 세컨더리가 되었다가 나중에 다시 도전한다.
  • 일반적으로 프라이머리가 다운됐다고 알리는데 최대 2초가 소용된 후 선출을 시작한다.

11.4 롤백

  • 프라이머리가 수행한 쓰기를 세컨더리가 복제하기 전에 프라이머리가 다운되면 다음에 선출되는 프라이머리에 해당 쓰기가 없을 의미한다.

예를 들어 네트워크 파티션이 각각 DC1, DC2 가 있다고 가정해보자.

이 두 파티션에서

DC1(주 연산#126)

  • 세컨더리 A
  • 세컨더리 B

DC2(주 연산 #125)

  • 프라이머리 C
  • 세컨더리 D
  • 세컨더리 E

연산 #126의 불일치:

  • DC2의 프라이머리 C는 네트워크 파티션이 발생하는 동안 연산 #125까지 수행
  • 반면, DC1에서는 어떤 방식으로든 연산 #126을 수행했을 수 있으나, DC1에는 프라이머리가 없어 쓰기 작업은 불가능

서로 다른 서버들간에 동기화를 시작하지만 DC2에서는 연산 #126을 찾지 못한다. (이러면 롤백이 생긴다.)

DC1에서는 해당 도큐먼트 버전을 rollback 디렉토리의 .bson에 작성한다. 그리고 프라이머리에서 해당 컬렉션명.bson을 복제한다.

  • MongoDB에서는 롤백을 수행할 때, 세컨더리 노드에서 프라이머리 노드로부터 동기화되었던 데이터 중 불일치가 발견된 데이터를 보존. 이 데이터는 rollback 디렉토리에 .bson 파일 형식으로 저장
  • 롤백 후, 세컨더리 노드는 프라이머리 노드로부터 최신의 정확한 데이터 상태를 다시 복제 이 과정에서 프라이머리 노드의 데이터베이스 내 특정 컬렉션(예: collectionName.bson)의 내용을 세컨더리 노드에 전송하여 동기화

다만, 몽고DB 4.0 이전에는 롤백할 수 있는 양이 제한적이었다면 (얼만큼인지는 모르겠다.) 4.0 이후부터는 데이터 양이 제한이 없다.

  • 300메가바이트 초과하거나 롤백에 시간이 30분이상 걸리면 실패할 수 있다.

'IT' 카테고리의 다른 글

트랜잭션  (0) 2024.05.26
resilience 4j 발표 내용 정리  (0) 2024.04.21
몽고 디비 9장  (0) 2024.04.01
Mongo 7 - 집계  (0) 2024.03.11
6장 - 키-값 저장소 설계  (0) 2024.02.12