Replication(2) - 다중리더와 리더리스 복제방법
단일 리더 가반의 시스템에서는 리더가 하나만 존재하고 모든 쓰기가 해당 리더를 거쳐야 한다. 하지만 네트워크 지연 등의 이유로 리더에 접근이 불가능 해지거나 쓰기 트래픽 부하가 증가하게 된다면 쓰기 작업을 하지 못하는 상황이 발생할 수 있다. 때문에 쓰기 작업이 많은 서비스의 경우에는 다중리더나 리더리스 복제방식을 고려해 볼 수 있다.
다중리더 기반 복제
다중리더 기반 복제는 쓰기 작업이 가능한 노드(리더)를 여러개 운영하는 방식이다. 쓰기 작업이 하나의 노드로 몰리지 않으니 쓰기 부하 분산의 효과와 가용성이 보다 뛰어나다는 큰 장점을 가지지만 같은 정보에 대해 여러 리더 노드에서 동시에 쓰기가 일어나거나, 네트워크 상황으로 인한 복제 지연으로 최신 데이터에 대한 정보가 잘못되는 문제와 같이 쓰기 충돌
에 대한 고려가 필요하다.
다중리더의 운영
- 다중 데이터센터를 두고 각 데이터센터마다 하나의 리더를 둔다.
- 각 데이터센터가 독립적으로 운영되고 리더간 통신으로 복제 및 데이터의 정합성을 맞춘다.
- 이때 데이터 충돌이 일어날 가능성이 있으므로 이를 해결하기도 해야한다.
- 각 데이터센터의 팔로워들은 해당 데이터센터의 리더로부터 데이터를 복제받는다.
단일리더와 비교한 다중리더의 장점
- 성능
- 여러 곳에서 쓰기가 가능하므로 유저가 지리지거으로 가까운 데이터센터에서 쓰기 작업을 할 수 있다.
- 데이터센터 중단 시 내성
- 각 데이터센터 마다 리더가 존재하므로 쓰기를 하지 못하는 상황은 일어나기 힘들다.
- 네트워크 문제 내성
- 일시적이 네트워크 중단에도 쓰기 처리를 잘 진행할 수 있다.
다중 리더기반 복제와 유사한 사례(비슷한 문제점이 존재하는 사례)
- 오프라인 작업을 하는 클라이언트
- 같은 파일을 오프라인 상태(네트워크 연결X)에서 여러 디바이스로 작업, 각 디바이스는 변경사항을 로컬에 저장
- 온라인에 연결되었을때 각 디바에스에서의 데이터를 동기화
- 각 디바이스의 로컬 데이터베이스를 하나의 데이터센터로 본다면 다중 리더 기반과 유사함
- 오프라인 상태 작업 = 데이터센터 간 네트워크 문제 발생 이라고 볼 수 있음
- 네트워크에 연결 후 각 데이터센터에서의 쓰기 작업을 취합하고 충돌을 해소해야함
- 동시 편집
- 하나의 페이지에서 여러 유저가 동시에 편집하는 상황
- 이 상황에서 쓰기 충돌이 일어난다.
- 한 사람이 쓰기를 할 때 다른 사람의 쓰기를 막으면 단일리더와 같은 상황이 됨
- 때문에 빠른 협업을 위해 변경 단위를 매우 작게해서 잠금을 피하면 충돌 해소가 필요한 경우를 필요해 다중 리더와 비슷한 문제를 야기한다.
쓰기 충돌 다루기
- 동기, 비동기 충돌 감지
- 동기식으로 만들면 단일 리더와 다를게 없음.(모든 서버가 쓰기가 복제되기를 기다리는 것이므로)
- 비동기식은 작업 이후 충돌을 감지한다. 다중 쓰기가 가능하지만 이러한 충돌을 해소하는 방법이 필요하다.
- 충돌 회피하기
- 특정 레코드의 쓰기를 같은 리더(물리적 거리 기반)에서 이뤄지도록 만들기
- 하지만 유저가 이동하거나 해당 데이터센터에 장애가 발생하면 다른 데이터센터를 이용해야 하며 결국 기존 데이터센터에 존재하는 데이터와 충돌이 발생한다.
- 일관된 상태로 수렴시키기(항상 최신데이터로 유지하는 방법)
- 고유 ID를 부여하고 이를 이용하거나 타임스탬프를 기반으로 데이터의 순서를 맞춘다. 하지만 네트워크 지연, 신뢰할 수 없는 시계열 등의 문제로 잘못된 순서로 정렬되어 데이터를 유실할 가능성이 있다.
- 충돌을 해소하는 시기
- 쓰기 수행 시 - 쓰기 수행 즉시 데이터가 충돌이 나는지 확인 후 수정, 리더의 부하가 증가 및 응답에 지연이 발생할 수 있다.
- 읽기 수행 시 - 쓰기 시에는 충돌을 해소하지 않고 이후 읽기 시에 해소하는 방법 (지연 해소)
- 자동 충돌 해소 방법
- Operational transformation(Figma, Google Docs가 사용하는 방법)
- CRDT(자료구조에 기반한 방법)
리더간 복제 방식
- 원형 토폴로지
- 각 리더를 원형으로 연결
- 무한 복제를 막기 위해 각 노드를 거치면서 복제 로그에 해당 노드의 식별자를 태깅한다.
- 하나의 리더가 고장나면 전파가 불가능함. 장애 노드를 회피하도록 재설정 할 수 있지만 수동임
- 별 모양 토폴로지
- 각 리더를 양방향 트리 모델로 연결
- 무한 복제를 막기 위해 각 노드를 거치면서 복제 로그에 해당 노드의 식별자를 태깅한다.
- 하나의 리더가 고장나면 전파가 불가능함. 장애 노드를 회피하도록 재설정 할 수 있지만 수동임
- 전체 연결 토폴로지
- 모든 리더가 서로 연결되어 있는 형태
- 내결함성이 가장 우수함
- 하지만 네트워크 지연시간으로 인해 복제로그가 이전 복제로그를
추월
할 수 있음. 따라서 복제에서도 일관된 순서로 읽기와 같은 인과성의 문제가 발생할 수 있다. - 이런 이벤트를 올바르게 정렬하기 위해
버전 벡터
라는 기술을 사용한다.
대부분의 데이터베이스에서 충돌 해소 기법이 제대로 구현되어 있지 않아 다중 리더를 사용하게 된다면 문서를 주의 깊게 읽고 철저하게 테스트 하는 것이 좋다.
리더리스 기반 복제
리더리스 기반 복제는 리더가 존재하지 않는 복제 모델로 AWS의 DynamoDB에서 사용한 후 유행하게 되었다. 리악, 카산드라, 볼드모트가 이 시스템을 사용하며 이런 종류의 데이터베이스들을 다이나모 스타일이라고 부르기도 한다.
리더리스 는 모든 노드에 읽기, 쓰기가 가능한 방법이다. 모든 노드에서 쓰기가 일어난다면 데이터의 일관성을 맞추기 어려울텐데 어떻게 이런 모델이 가능한 것일까?
리더리스의 동작 방식 - 정족수 일관성
리더리스는 클라이언트가 읽기 및 쓰기 작업시 N개의 복제 서버에 모두 요청을 보낸다. 몇 노드에는 성공적으로 쓰기가 이뤄지고 쓰기에 실패하는 노드가 있을 수도 있다. 각 노드들은 데이터에 버전을 가지고 있기 때문에 실패 시 구 버전을 그대로 가지고 있을 것이다. 사용자가 데이터를 읽을 때에도 N개의 노드에 요청을 보내고 요청을 응답을 받았을 때 구버전의 데이터를 가진 노드(쓰기에 실패했던 노드)에 데이터를 새버전으로 업데이트한다.
쓰기를 할때 모든 노드가 성공했을때를 성공으로 보지않고 특정 갯수(W)의 노드에 성공적으로 쓰여진다면 쓰기 성공으로 간주한다. 읽기의 경우에는 특정 갯수(R)의 노드에 질의하고 가장 최신값을 읽는다. 보통 W와 R의 갯수는 N < W + R
공식으로 잡는다.(정족수 읽기 쓰기) 이렇게 하면 R개의 노드 중 적어도 하나에서는 가장 최신 값을 읽을 수 있기 때문이다.
정족수 일관성에도 한계는 존재한다.
- 느슨한 정족수를 사용한다면 R개의 노드와 W개의 노드가 겹치는 것을 보장하지 않음
- 두 개의 쓰기 발생 시 어떤 쓰기가 먼저 일어났는지 분명하지가 않다. 해당 충돌을 해결해야한다.
- 쓰기, 읽기가 동시에 발생하면 쓰기는 일부 복제 서버에만 발생할 수 있어 읽기에서 최신값을 받았는지 확인이 불분명하다.
- 쓰기가 일부 복제서버에서 디스크 용량 부족과 같은 문제로 실패해도 롤백하지 않는다. 읽기 시 혼란이 올 수도 있다.
노드가 고장났을때 복구방법
복제는 최종적으로 모든 데이터가 모든 복제 서버에 복제되어야 한다. 장애로 노드가 중단된 후 온라인 상태가 되었을때 그 사이 누락된 쓰기를 어떻게 따라잡을 수 있을까?
- 읽기 복구 읽힐 때 클라이언트 측에서 최신값 업데이트
- 안티 엔트로피 처리(잘 안씀) 백그라운드 프로세스를 두고 복제 서버간 데이터 차이를 지속적으로 찾아 복사(상당한 지연이 발생할 수 있음)
정족수가 충족되지 못하는 문제 해결 - 느슨한 정족수, 암시된 핸드오프
N개의 노드 중 쓰기일 땐 최소 W개에서 응답을 받아야하며, 읽기일땐 최소 R에 질의를 보내야 한다. 하지만 N개 중 장애 노드가 여러 발생하거나 네트워크 문제로 W개보다 작아질 때가 있다. 이 상황에서는 정족수를 충족할 수 없다.
이때 클러스터에서 N에 들어가지 않는 일부 데이터베이스(다른 파티션의 데이터베이스)를 차출하여 연결하고 이 노드에 데이터를 저장한다. 이러한 상황을 느슨한 정족수라고 부른다. 그리고 장애 상황이 해결되면 이 노드에 저장된 데이터를 장애가 발생했던 노드들에 전송한다. 이 방식을 암시된 핸드오프라고 부른다. 하지만 위 상황에서 데이터 읽기 시 N에 포함되지 않는 임시로 차출된 노드에 접근한다는 보장이 없기 때문에 일관성에 문제가 생길 수 있다. 때문에 느슨한 정족수 다이나모 스타일에서도 선택 사항이다.
동시 쓰기 감지 & 병합 방법
이전 발생 관계인지 동시성 인지 파악하고 동시성의 경우 클라이언트에게 책임을 미루는 것이 다이나모 스타일의 데이터 병합 방식이다.
- 서버측에서 데이터에 버전을 명시함
- 이전 발생 관계인 경우 서버측에서 새로운 버전으로 업데이트함
- 동시 작업인 경우 병합하지 않고 새로운 버전으로 만들고 병합되지 않은 이전 버전도 함께 클라이언트에게 응답함
- 클라이언트는 이후 새로운 요청을 보낼때 새로운 값과 함께 병합할 버전, 현재 자신이 가지고 있는 버전도 함께 요청에 보냄
- 서버에서는 3개를 모두 합쳐 새로운 버전을 만들어냄 - 최종적으로 데이터가 병합됨
- 결과적으로 클라이언트에서 버전을 가지고 병합 처리 요청을 진행해야함
이러한 매커니즘으로 데이터당 버전 번호를 매길 뿐만 아니라 각 데이터베이스 복제본에도 버전을 매겨야 한다. 데이터의 버저닝과 복제본의 버저닝을 N개의 버전을 통해 데이터 충돌을 해소하는 방법을 버전벡터
라고 부른다.
💡 이전 발생 관계와 동시성의 차이
A라는 작업이 B라는 작업을 알고 진행한다면 이것은 이전 발생 관계가 있는 것이다. 하지만 A 작업이 B 작업의 존재를 모르고 B작어도 A라는 작업을 모르는 채로 작업이 진행되었다면 동시 작업이라고 할 수 있다.
🤔 클라이언트의 역할이 많아 지는 문제
리더리스에서는 클라이언트가 모든 노드에 병렬 호출을 할뿐만 아니라 일관성을 확인 후 맞지 않는 노드를 업데이트 해야하는 역할을 맡는다. 클라이언트의 역할이 많아져 이를 대신할 코디네이터 노드를 두기도 한다. 코디네이터 노드가 기존 클라이언트가 맡는 역할을 대신해주며 단일 리더, 다중 리더에서 읽기 시 적절한 팔로워로 요청을 라우팅 하는 역할을 해주기도 한다.
🤔 리더가 존재하는 시스템에서의 어떻게 읽기 요청을 적절한 팔로워로 라우팅 시킬 수 있을까?
코디네이터 노드가 먼저 요청을 모두 받아 읽기 유저 ID기반, 지리기반으로 적절한 팔로워로 라우팅 시킬 수 있다.
🤔 코디네이터가 SPOF가 되는 문제?
코디네이터는 요청 라우팅 등의 역할을 맡는 일종의 프록시이다. 하지만 이 코디네이터는 SPOF(단일 장애 지점)이 될 수 있어 노드 중단 시의 대책이 필요하다. 리더 선출과 마찬가지로 시스템 내 다른 노드가 코디데이터로 선출될 수 있도록 설계하는 것이 중요하다.
🤔 리더리스의 장점 정리++
일관성 관리, 복잡한 분산 트랜잭션 지원
🤔 리더리스에서 N개 모두에 읽기, 쓰기를 보낸다면 부하 분산의 효과는 없지 않을까?
다수의 노드에게 쓰기와 읽기 요청을 보내므로 부하 분산의 측면에서는 다른 시스템에 비해 제한적이다.
댓글남기기