DB - MongoDB 복제(Replica-set,프라이머리,세컨드리,아비터노드)

2019. 9. 12. 19:16Database/MongoDB

 

몽고디비(MongoDB)에서는 마스터-슬레이브 복제와 레플리카 셋 복제라고 하는 두 가지 방식의 복제를 지원한다. 마스터-슬레이브 복제는 몽고디비가 만들어졌던 초기에 사용하던 복제 방식으로, 몽고디비3.2 버전에서는 권장하지 않는 방식이다. 또한 마스터-슬레이브 복제 방식은 마스터의 장애에 대한 페일오버를 관리자가 수동으로 처리해야 하며, 최근 버전의 몽고디비에서는 거의 기능이 개선되거나 보완되지 않고 있다. 그에 반해서 레플리카 셋 복제는 안정되고 많은 부분 자동화되어 처리될 수 있게 개발됐다.

 

복제란?

복제는 여러 서버가 서로의 데이터를 동기화하는 것을 의미하는데, 서로 주고받는 데이터에 따라서 논리 복제와 물리 복제로 나눌 수 있다. DRBD(Distributed Replicated Block Device)와 같이 리눅스 서버가 데이터의 내부를 전혀 모르는 상태에서 디스크의 블록만 복제하는 형태를 물리적 복제라고 한다. 그리고 MySQL 서버나 MongoDB 서버와 같이 데이터베이스 서버가 직접 서버간의 데이터를 동기화하는 방식을 논리적 복제라고 한다. 물리적 복제는 데이터베이스가 전혀 관여하지 않으므로 운영체제 차원에서 응용 프로그램에 투명하게 복제를 처리할 수 있지만, 응용 프로그램의 캐시나 내부 처리 로직에서 변경된 데이터를 사용하지 못하는 문제점도 있다. 

 

몽고디비는 MySQL 서버의 복제와 아주 비슷하게 논리적 복제 수단을 가지고 있으며, 더불어 프라이머리 노드의 선출과 네트워크 단절로 인한 스플릿 브레인(Split-Brain) 현상을 막을 수 있는 기능까지 내장하고 있다.

 

컨센서스 알고리즘(Consensus Algorithm)

여러 서버가 복제에 참여해서 서로 같은 데이터를 동기화하는데, 이렇게 데이터를 공유하는 그룹을 레플리카 셋이라고 한다. 그리고 하나의 레플리카 셋에는 프라이머리와 세컨드리 노드로 각자의 역할이 나뉜다. 레플리카 셋이 참여하는 각 멤버들은 각자의 역할에 맞게 작동하면서 서로의 데이터를 동기화하고, 특정 노드가 응답 불능이 됬을 때 어떻게 대처할 것인지 등을 결정한다. 이때 어떻게 작동할지 결정하는 것을 켄센서스 알고리즘이라고 하는데, 몽고디비는 확장된 형태의 Raft 컨센서스 모델을 사용한다.

 

켄센서스 알고리즘은 크게 Paxos와 Raft 컨센서스 알고리즘이 있다.

 

Raft 켄센서스 알고리즘의 가장 큰 특징은 리더 기반의 복제와 각 멤버 노드가 상태를 가진다는 것이다. 하나의 레플리카 셋에는 반드시 하나의 리더만 존재할 수 있고, 리더는 사용자의 모든 데이터 변경 요청을 처리한다. 그리고 리더는 사용자의 요청 내용(데이터 변경)을 로그에 기록하고, 모든 팔로워는 리더의 로그를 가져와서 동기화를 수행한다. Raft 켄센서스 알고리즘의 리더를 몽고디비에서는 프라이머리 노드라고 하고 팔로워는 세컨드리 노드라고 한다. 그리고 로그를 몽고디비에서는 OpLog(Operation Log)라고 한다.

 

복제의 목적은?

복제의 가장 큰 목적은 동일한 데이터를 이중 삼중으로 유지함으로써 레플리카 셋의 특정 멤버에서 데이터 손실이 발생하더라도 다른 멤버의 데이터로 대체할 수 있도록 하기 위함이다. 즉 고가용성(High-Availability)을 위해서 중복된 데이터 셋을 준비하는 것이다.

 

고가용성을 위해서 몽고디비 레플리카 셋의 각 멤버는 서로 다른 멤버가 살아있는지 계속 확인하는 하트비트 메시지를 주고 받는다. 만약 프라이머리 노드와 통신되지 않으면 새로운 프라이머리 노드를 선출해 서비스가 계속 지속되도록 한다.

 

복제의 또 다른 목적으로는 데이터조회 쿼리의 로드 분산을 생각할 수 있다. 몽고디비에서 데이터의 변경은 프라이머리 노드에서 시작되지만, 각 세컨드리 노드는 데이터변경은 하지 못하지만 조회쿼리의 요청은 받을 수 있다. 즉, 읽기 성능을 높히기 위해 세컨드리 노드를 확장해서 사용하기도 한다.(노드를 늘림)

 

레플리카 셋에서 조금은 특이한 역할을 노드가 존재한다. 이 노드는 아비터노드라고 한다. 아비터노드는 데이터를 가지고 있지 않고 단순히 프라이머리 선출을 위한 투표에 참가할 뿐이다.(정족수==쿼럼을 채우기 위함) 그렇다면 이런 노드는 왜 사용하지라고 생각할 수 있지만 하나 상황을 가정해보자.

 

하나의 레플리카 셋은 반드시 과반수 이상의 멤버가 투표에 참여해서 승인을 얻어야 프라이머리 멤버를 선출할 수 있다. 즉 과반수 이상의 멤버가 살아있어야 사용자의 쓰기 요청을 처리할 수 있음을 의미한다. 만약 과반수 이상의 멤버가 응답 불가능한 상태에는 프라이머리를 선출할 수 없으며, 레플리카 셋의 모든 멤버가 세컨드리로 남게된다. 이런 급박한 상황에 레플리카 셋의 멤버를 투입하는 것은 상당히 많은 시간이 필요하다. 왜냐하면 새로운 노드는 레플리카 셋의 데이터를 가져야하기에 이러한 데이터 동기화를 위한 비용이 들기 때문이다. 만약 데이터가 작다면 상관없지만 아주 크다면 레플리카 셋에 참여하는 새로운 멤머가 데이터를 가지기 위해 리밸런스가 일어날 것이고 엄청난 시간이 소요될 것이다. 이런 경우 아비터 노드로 레플리카 셋에 멤버를 추가하면 데이터를 위한 비용은 거의 들이지 않고 빠르게 투표에 참여할 수 있게 되어 서비스 정상화를 위한 시간이 아주 빨라진다.

 

그래서 보통 몽고디비 고가용성을 보장하기 위해 레플리카 셋은 최소 3개로 구성하며 프라이머리노드,세컨드리노드,아비터노드 이렇게 3개의 노드로 레플리카 셋을 구성한다. 만약 프로덕 환경에 아비터를 사용하는 경우에는 반드시 아비터를 위한 전용 서버를 고려하는 것이 좋다. 아비터는 별도로 사용자의 데이터를 보관하거나 처리하지 않기 때문에 높은 사양의 하드웨어를 필요로 하지 않기 때문이다. 아니면 아비터 서버들을 위한 별도 서버를 마련하여 아비터 인스턴스 여러개를 전용 서버에서 구동시켜도 좋다.

 

이전 포스팅에서도 거론했지만 다시 한번 하나의 레플리카 셋에 1개 이상의 아비터 노드는 구성하지 말자.

 

레플리카 셋 노드 종류

  1. 프라이머리 노드
  2. 세컨드리 노드
  3. 아비터 노드

 

이번 포스팅에서는 내부적인 동작방식은 다루지 않는다. 나중에 기회가 된다면 리더 선출 및 기타 내부적인 구동 방식을 다루어 볼것이다.