[MongoDB] Ch11 - 복제 셋 구성 요소
복제 셋 조각들이 어떻게 조합되는지 살펴보자
- 복제 셋 멤버가 새로운 데이터를 복제하는 방법
- 새로운 멤버를 영입하는 방법
- 선출이 작동하는 방법
- 발생할 수 있는 서버 및 네트워크 오류 시나리오
11.1 동기화
복제는 여러 서버에 걸쳐 복사본을 보관하는 데 관련 있다
몽고DB는 프라이머리가 수행한 쓰기를 모두 포함하는 로그, 즉 oplog를 보관함으로써 복제를 수행한다
oplog : 프라이머리의 로컬 데이터베이스에 있는 제한 컬렉션
각 세컨더리는 프라이머리로부터 복제한 작업을 각각 기록하는 oplog를 보관한다
동기화 소스로 사용되도록 한다
세컨더리 서버가 종료되었다 다시 시작되면 oplog에 있는 마지막 연산과 동기화한다
일반적으로 oplog는 기본 크기면 충분하다
oplog를 만들기 전에 oplogSizeMB 옵션을 사용해 크기를 지정할 수도 있다
몽고DB에서 데이터 동기화는 2가지 형태다
(1) 초기 동기화
복제 셋의 한 멤버에서 다른 멤버로 모든 데이터를 복사한다
- 먼저 동기화를 시작하기 전 유효한 상태인지 확인한다
- 유효하면 복제 셋의 다른 멤버의 데이터 전체를 복사한다
몽고DB는 local 데이터베이스를 제외한 모든 데이터베이스를 복제한다
기존 데이터는 복제 작업을 시작하기 전에 삭제된다
(데이터를 모두 삭제한 후 복제한다)
몽고DB 3.4 이후 버전부터 복제할 때 인덱스도 같이 구축한다 (이전 버전에는 _id 인덱스만 구축)
모든 데이터베이스가 복제되면 mongod는 소스의 oplog를 사용해 복제 셋의 현재 상태를 반영하도록 갱신하고, 복사가 진행되는 동안 발생한 데이터셋에 모든 변경 사항을 적용한다
초기 동기화는 운영자 관점에서 쉬운 작업이다 (비어있는 디렉터리에서 mongod를 시작하면 된다)
하지만 나중에 나오는 백업으로부터 복원하는 방식이 더 바람직하다
💡 백업으로부터 복원하면 mongod를 통해 모든 데이터를 복사할 때보다 빠를 때가 많다
또한 복제는 동기화 소스의 작업 셋을 망칠 수 있다
초기화를 수행하면 해당 멤버는 자주 사용되는 데이터를 축출해 메모리로 페이징하며, 이는 멤버가 급격하게 느려지게 한다
램에 있는 데이터가 처리하던 요청들이 갑자기 디스크로 향하기 때문이다
서버에 여유 공간이 있고 데이터셋이 작다면 초기 동기화는 쉽고 좋은 방법이다
초기 동기화를 수행하면 수행시간이 오래걸리는 문제가 많이 발생한다
이때 새로운 멤버는 동기화 소스의 oplog 끝부분으로 밀려날 수 있고, 동기화 소스를 따라잡을 수 없을 정도로 뒤쳐져버린다 (동기화 소스의 oplog는 새로운 멤버가 계속 복제해야 하는 데이터를 덮어쓰기 때문이다)
이런 문제를 해결하려면 덜 바쁜 시간에 초기 동기화를 시도하거나 백업으로부터 복원하는 방법을 사용해야한다
(2) 복제
몽고DB가 수행하는 두 번째 동기화 유형은 복제다
세컨더리 멤버는 초기 동기화 후 지속적으로 데이터를 복제한다
동기화 소스에서 oplog를 복사한 후 이러한 작업을 비동기 프로세스에 적용한다
실효 처리
세컨더리는 동기화 소스상에서 수행된 실제 연산들보다 훨씬 뒤떨어지면 곧 실효 상태가 된다
동기화를 계속 진행할 경우 일부 작업을 건너뛰게 된다
세컨더리가 다운타임중이거나, 쓰기 요청이 처리량을 뛰어넘거나, 읽기 작업 때문에 매우 바쁠 때 발생한다
세컨더리가 동기화되지 못하는 상황을 피하려면, 프라이머리가 많은 양의 연산 이력을 보관하도록 큰 oplog를 가져야 한다
큰 oplog는 당연히 더 많은 디스크 공간을 사용하지만 충분히 감수할 만하다
디스크 공간은 저렴한 편이고 대개는 oplog의 적은 부분만 사용 중이므로 램을 많이 차지하지 않는다
일반적으로 oplog는 2~3일 분량의 정상적인 연산에 대해 적용 범위를 제공해야 한다
11.2 하트비트
멤버는 복제 셋에 대한 최신 정보를 유지하기 위해 복제 셋의 모든 멤버로 2초마다 하트비트 요청을 보낸다
하트비트의 가장 중요한 기능은 복제 셋의 과반수 도달 가능 여부를 프라이머리에게 알리는 기능이다
프라이머리가 더는 서버의 과반수에 도달할 수 없다면, 스스로 강등해 세컨더리가 된다
(1) 멤버 상태
STARTUP
멤버를 처음 시작할 때의 상태
몽고DB가 멤버의 복제 셋 구성 정보 로드를 시도할 때 이 상태가 된다
STARTUP2
구성 정보가 로드되면 STARTUP2 상태로 전환된다
초기 동기화 과정 전반에 걸쳐 지속된다 (일반적으로 단 몇초 동안만 지속)
복제와 선출을 다루기 위해 몇몇 스레드로 분리되며 다음 상태인 RECOVERING으로 변환된다
RECOVERING
멤버가 현재 올바르게 작동하지만 읽기 작업은 수행할 수 없음을 의미한다
조금 과부하된 상태로 다양한 상황에서 나타난다
세컨더리가 되기 전에 짧게 RECOVERING 상태를 거친다
멤버는 조각 모음 같은 긴 연산이 진행될 때나 replSetMaintenance 명령에 대한 응답으로 RECOVERING 상태가 될 수 있다
또한 다른 멤버들보다 너무 많이 뒤처질 때 따라잡기 위해 RECOVERING 상태가 될 수 있다
일반적으로 멤버 재동기화가 요구되는 장애 상태이긴 한데, 이 시점에서 해당 멤버는 오류 상태가 되지는 않는다
해당 멤버가 스스로 유효 상태로 돌아갈 수 있도록 누군가 충분히 긴 oplog를 갖고 올 것이라고 기대하기 때문이다
ARBITER
일반적인 연산 중에는 특수한 상태인 ARBITER를 유지해야 한다
또한 다음과 같이 몇몇 상태는 시스템상 문제를 나타낸다
DOWN
멤버가 살아 있지만 네트워크 문제 때문에 도달할 수 없는 상태
UNKNOWN
멤버가 다른 멤버에 도달한 적이 없었다면 상태를 전혀 알 수 없으므로 UNKNOWN으로 알린다
일반적으로 unknown member가 다운되거나 두 멤버 간에 네트워크 문제가 있음을 나타낸다
REMOVED
멤버가 복제 셋으로부터 제거된 상태
ROLLBACK
멤버가 데이터를 롤백할 때 사용된다
롤백 과정 마지막에서 서버는 RECOVERING 상태로 전환되고 세컨더리가 된다
11.3 선출
멤버가 프라이머리에 도달하지 못하면 프라이머리 선출을 모색한다
선출되고자 하는 멤버는 모든 멤버에 알림을 보낸다
자격이 없으면 반대 있으면 찬성에 투표를 한다
해당 멤버가 복제 셋의 과반수로부터 득표하면 선출은 성공적으로 이뤄지고 프라이머리 상태로 전환된다
반면 과반수 득표를 하지 못하면 멤버는 세컨더리 상태로 남으며 나중에 다시 프라이머리가 되려고 시도한다
프라이머리는 멤버의 과반수에 도달할 수 없거나, 다운되거나, 세컨더리로 강등되거나, 복제 셋이 재구성될 때까지는 자격을 계속 유지한다
11.4 롤백
롤백은 복구 전에 복제되지 않은 연산을 원래 상태로 되돌리는 데 사용된다
롤백이 완료되면 해당 서버는 RECOVERING 상태로 전환되고 다시 정상적으로 동기화를 시작한다
롤백이 실패할 경우
몽고DB 4.0 이전 버전에서는 롤백이 수행되기에 너무 큰지 여부를 결정할 수 있었지만, 이후로 롤백할 수 있는 데이터 양에 제한이 없다
4.0 이전 버전에서는 데이터가 300MB 초과하거나 시간이 30분 이상 걸리면 실패할 수 있다
(이때는 롤백에 갇힌 노드를 재동기화해야 한다)
이런 현상은 세컨더리가 뒤처지고 프라이머리가 다운될 때 가장 흔히 발생한다
세컨더리 중 하나가 프라이머리가 되면 이전 프라이머리로부터 많은 연산을 놓치게 된다
멤버가 롤백에 갇히지 않게 하려면 세컨더리를 가능한 한 최신 상태로 유지하는 것이 좋다