Replication 데이터 변경의 문제

복제(Replication)란 네트워크로 연결된 여러 장비에 동일한 데이터의 복사본을 유지한다. 복제가 필요하거나 사용하는 이유는 다음과 같다.

  • 지연시간 down - 지리적으로 사용자와 가깝게 유지
  • 가용성 up - 시스템 일부에 장애가 발생해도 지속적으로 동작
  • 읽기 처리량 up - 읽기 제공 장비 수를 확장해 처리량 늘림

❗️ 파티셔닝(샤딩)의 개념을 제외하고 복제에 대한 개념만을 정리한다.
즉, 전제 데이터셋을 하나 장비에 모두 저장한다고 가정한다.

하지만 복제에서는 데이터의 변경과 변경 전파로 인해 여러 어려움이 발생한다. 때문에 데이터 변경을 복제하기 위한 여러가지 알고리즘이 제시 되었으며, 대표적으로 단일 리더, 다중 리더, 리더리스(leaderless)가 존재한다.

단일 리더(리더 - 팔로워)

  • 하나의 리더와 여러개의 복제 서버(팔로워)가 존재
  • 리더에만 쓰기 작업 가능, 팔로워는 읽기 작업만 수행
  • 쓰기 작업은 적지만 읽기 작업이 많은 시스템에 적합
  • 리더가 하나만 존재하고 모든 쓰기가 리더를 거쳐야만 하므로, 네트워크 문제와 같은 이유로 리더에 접근하지 못한다면 쓰기 작업이 불가능하다는 단점이 존재

데이터 변경을 팔로워로 복제하는 매커니즘

  • 동기식 - 하지만 완전한 동기식은 가용성(Availability)을 떨어트린다.

💡 가용성이란?
어떤 문제나 일이 발생하여도 클라이언트에게 멈춤없이 정보를 제공해줄 수 있는지를 의미한다. 가용성이 높다는 것은 시스템 일부에서 문제가 생기더라도 클라이언트가 정보를 받는데 문제가 없다는 의미이다.

  • 비동기식 - 완전한 비동기식은 일관성(Consistency)을 떨어트린다.

💡 일관성이란?
어떤 상황에서라도 언제나 같은 데이터를 얻을 수 있음을 의미한다. 리더에 데이터 쓰기 작업이 후 복제 서버로 데이터 변경 전파가 일어난다. 이때 A 복제 서버에 데이터 변경 전파가 완료되기 전에 A 복제 서버로 읽기 요청을 보낸다면, 리더에서 읽은 데이터와 데이터 불일치 문제가 발생한다. 이러한 상황을 ‘일관성 문제가 발생했다’고 한다.

  • 반동기식
    • 하나의 복제 서버에만 동기식으로 동작하고 나머지 복제서버에는 비동기식으로 동작하는 방식이다.
    • 동기식으로 동작하는 복제 서버로 전파가 완료되면 클라이언트에게 응답한다.
    • 강한 일관성을 포기하고 가용성과 함께 최종적 일관성을 얻을 수 있다.

❓ 동기식으로 동작하는 복제서버에 문제가 발생한다면?
네트워크 이슈나 장애로 인해 해당 복제서버로의 전파가 제대로 이루어지지 않는다면 반동기식도 가용성에 문제가 발생할 것이다. 이런 문제를 사전에 감지하거나 문제가 발생하게 된다면 어떻게 조치할 수 있을까? 타임아웃 방식?

팔로워 증설 및 교체 시 데이터 복제

읽기 처리량이 늘거나 기존의 복제 서버에 장애가 발생하여 새로운 팔로워를 만들어야하는 경우가 있다. 여기서 새롭게 만들어지는 팔로워는 대량의 원본 데이터들을 받아야한다. 이와 동시에 전체 시스템이 중단되지 않는 상태에서 진행되어야 한다.

  1. 리더가 주기적으로 백업한 스냅샷을 일정 시점에서 가져온다.
  2. 새로운 팔로워가 이 스냅샷을 복사한다.
  3. 새로운 팔로워는 리더와 연결하고 이 스냅샷 이외에 발생한 데이터 변경 건들을 요청한다.
  4. 팔로워가 모든 변경건을 처리하게된다면, 이제부턴 다른 팔로워와 마찬가지로 리더에 발생하는 데이터 변화를 전파받아 처리할 수 있다.

노드 장애 발생! 장애 복구 처리

어떤 노드에 장애가 발생했느냐에 따라 그 과정이 복잡할 수 있다. 우선 팔로워 노드에 장애가 발생한다면 비교적 장애복구가 쉽다. 팔로워 노드는 동작 시 리더로부터 수시된 데이터 변경 로그를 로컬 디스크에 보관한다. 만약 팔로워 노드에 장애가 발생하여 중단된다면, 재시작 시 데이터 변경 로그에서 마지막 처리한 트랜잭션을 확인한다. 그리고 리더에게 해당 트랜잭션 이후의 데이터 변경을 모두 요청한다. 이 변경이 모두 적용되고나면 이전과 같이 데이터 변경 스트림을 받아 처리한다.

하지만 리더에 장애가 발생한다면 그 과정이 까다롭다.(리더의 장애 복구 과정을 Fail over라 부른다) 장애 복구를 위해 신경써야 하는 부분이 많다.

  • 팔로워 중 하나를 새로운 리더로 승격
  • 클라이언트는 새로운 리더로 쓰기 전송이 일어나도록 재설정
  • 팔로워들은 새로운 리더로부터 데이터 변경을 소비하도록 재설정

Fail Over의 단계는 아래와 같이 진행된다.

  1. 리더의 장애 판단
    • 장애를 판단할 확실한 방법이 없기 때문에 단순히 타임아웃을 사용한다.
    • 노드 간 자주 메시지를 주고받으며 일정 시간 동안 응답이 없다면 죽은 것으로 판단한다.
  2. 리더 선출
    • 합의 알고리즘을 통해 팔로워 중 하나를 리더로 승격시킨다.
  3. 클라이언트의 쓰기 전송을 새로운 리더로 재설정
    • 요청 라우팅을 통해 기존 리더에서 새로운 리더로 쓰기연산을 전송할 수 있도록 조정한다.
  4. 팔로워들이 새로운 리더를 바라보도록 설정
    • 기존 장애로 판단된 리더 -> 새로운 리더를 바라보도록 시스템 재설정
    • 기존 장애 중단되었다가 재시작된 이전 리더가 본인이 이젠 리더가 아님을 인식할 수 있도록 해줘야함

🤔 리더 선출을 위한 합의 알고리즘, 요청 라우팅에 대한 공부가 필요

위 단계로 Fail over가 쉽게 해결된다면 좋겠지만 현실은 그렇지 못하다.

🤔 비동기식 데이터 복제를 사용 시 문제점
이전 리더가 실패하기 전의 쓰기 작업이 팔로워들에게 전파되지 못한 상황.
새로운 리더가 선출되고 이전 리더가 재가동 후 팔로워로 클러스터에 다시 추가되었을때, 해당 데이터가 충돌될 가능성이 있다. 가장 일반적인 해결책은 이전 리더의 복제되지 않은 쓰기를 단순히 폐기하는 방법이다. 하지만 이는 클라이언트의 기대를 저버리게 되는 문제가 있다.

🤔 리더 노드가 2개가 될 가능성
이전 리더가 클러스터로 복구 시 본인이 리더가 아님을 인식하지 못한다면 새롭게 선출된 노드와 이전 노드가 스스로 리더라고 믿는 상황이 발생한다. 이러한 상황을 스플릿 브레인이라 한다. 이 상황은 두 리더가 쓰기를 받으면서 충돌 해소 과정을 거치지 않는다면 데이터가 유실되거나 오염되는 문제가 발생한다. 이 문제를 해결하기 위해 펜싱 이라고 불리는 메커니즘과 잠금 메커니즘 등이 존재한다.

🤔 리더가 확실하게 죽었다고 판단 가능한 적절한 타임아웃은?
일시적인 부하 급중으로 패킷이 지연되는 경우 등을 문제로 설정된 타임아웃보다 응답이 지연되는 경우가 존재한다. 만약 부하로 인한 지연이 문제 였는데 리더가 죽었다 판단하여 장애복구를 진행한다면 오히려 상황이 더 악화될 수 있다.

팔로워로 데이터 변경을 전파시키는 방법들

리더에 데이터 변경 작업이 일어나면 복제 서버로 이를 전파해야 한다. 이때 복제 서버로 변경된 데이터를 전파하는 다양한 방식이 존재한다.

  • 구문 기반 복제 - 변경 쿼리가 일어났을때 해당 구문(Insert, update delete)을 그대로 복제 서버로 전달한다.

🤔 쿼리 내 비결정적 함수가 있다면 리더와 팔로워의 데이터 불일치 문제가 발생
쿼리 실행 시 해당 시간이나 난수를 생성하는 비결정적 함수가 있다면, 리더에서 실행한 것과 팔로워에서 실행한 데이터가 다를 가능성이 높다. 때문에 리더에서 해당 작업들을 미리 계산하여 상수로 바꾼 후 팔로워로 전파한다.

  • 쓰기 전 로그 배송 - 모든 쓰기는 db의 로그에 기록된다. 이 로그를 복제서버로 전파한다.

🤔 리더와 읽기DB의 버전이 다른 경우 충돌 문제
로그는 저수준의 데이터를 기술한다. 저장소 엔진과 밀접하게 엮이기 때문에 DB의 버전이 다르면 문제가 발생할 수 있다.

  • 논리적(로우 기반) 로그 복제 - db의 시스템 로그가 아닌 다른 로그 형식을 사용한다.
    • 엔진 내부와 분리함으로써 하위 호환성을 더 쉽게 유지할 수 있다.
  • 트리거 기반 복제 - DB가 지원하는 트리거나 스토어드 프로시저를 이용해 어플리케이션 코드를 등록하고 해당 코드에서 복제서버로 데이터 변경을 전파한다.
    • 다른 기법들에 비해 오버헤드가 크지만 보다 유연한다.(로그를 직접 커스텀 가능하므로)

복제 지연으로 인한 문제

보통 리더가 팔로워로 데이터 복제하는 과정은 비동기, 반동기로 작업한다. 동기식 작업은 여러 팔로워에게 전파되는 과정 중 네트워크 지연이나 여타의 이유로 지연이 되게 된다면 시스템 전체가 지연되기 때문이다.

하지만 비동기, 반동기에서는 일관성의 문제가 발생한다. 클라이언트가 변경작업 이후 해당 작업을 읽어올 때, 아직 데이터 변경이 전파되지 않았다면 이전 결과를 얻을 수 있다. 이런 불일치는 일시적인 상태에 불과하고 시간이 흐르면 팔로워가 결국 리더의 변경사항을 따라잡게 된다. 이런 효과를 최종적 일관성이라고 한다. 즉, 결국에는 일치가 된다는 의미이다.

대부분의 경우 리더와 팔로워 사이의 지연은 매우 짧은 순간이다. 하지만 부하 증가나 네트워크 문제로 인해 지연이 발생하게 된다면 지연시간은 증가할 수 있다. 이런 상황. 즉, 복제 지연으로 인해 몇가지 문제가 발생할 수 있다.

  • 자신이 쓴 내용을 읽기
    데이터변경 이후 곧바로 내 정보를 읽을때 팔로워에 해당 변경 사항이 전파되지 않아 데이터가 없거나 이전 데이터가 불러와지는 문제가 발생

💡 쓰기 후 읽기 일관성이 필요
사용자가 쓰기 후 페이지 재로딩 시 항상 자신이 제출한 모든 갱신을 볼 수 있음을 보장하는 것이다. (다른 사용자에 대해서는 보장하지 않는다.)

👍 사용자가 수정한 내용을 읽을 때는 리더에서 읽기
사용자가 수정 가능한 부분에 대해서는 항상 리더에서 읽도록 설정할 수 있다. 예르 들어 본인 프로필 요청과 같은 것들은 유저가 수정 가능한 부분이므로 항상 리더에서 읽도록 설정할 수 있다.

🤔 앱 내의 대부분의 컨텐츠가 사용자가 편집할 가능성이 있다면?
이와 같은 경우에는 트래픽이 리더로 몰리므로 팔로워의 효용성이 사라져 비효율적인 상황이다. 이러한 문제로 마지막 갱신 시각을 찾아서 이후 몇 분 동안만 리더에서 읽기를 수행하고 그 이후에는 팔로워로 읽기를 수행시킨다. 이때 클라이언트가 마지막 갱신 시간을 기억한다.

🤔 여러 데이터센터로 운영 중일 때 요청 라우팅 문제
리더와 팔로워를 여러 데이터센터에 걸쳐 사용중이라면 리더가 제공해야 하는 모든 요청은 리더가 있는 데이터센터로 라우팅 되어야 한다.

🤔 사용자가 여러 디바이스를 사용하는 문제
사용자가 여러 디바이스를 사용하는 경우에는 클라이언트가 마지막 갱신시간을 기억하는 것은 무의미하다. 다른 디바이스로 접속해 자신이 변경한 데이터를 요청할 수 있기 때문이다. 때문에 유저 아이디를 기반한 라우팅을 생각해야할 수도 있다.

  • 존재했던 데이터가 사라지는 상황 사용자A가 데이터를 변경하고 해당 변경사항이 팔로워(복제서버)A로 전파 완료하였고 팔로워B로는 아직 전파되지 않은 상황에서 사용자 B가 처음 접속햇을때는 팔로워A에서 정보를 얻었지만 곧바로 새로고침 시에는 팔로워B에서 정보를 얻어 사용자B 입장에서는 있었던 데이터가 사라져 안보이는 상황이 발생할 수 있다.

💡 단조 읽기가 필요
사용자 ID의 해시를 기반으로 팔로워를 선택한다. 항상 해당 팔로워로 요청을 보낸다.

🤔 사용자B와 매핑된 복제 서버에서 장애가 일어난다면?
사용자가 사용중이던 팔로워에 장애가 발생한다면 다른 복제 서버로 재라우팅 시키는 작업이 필요하다.

  • 데이터의 순서가 뒤바뀌는 상황(인과성의 문제) 복제보다는 샤딩(데이터를 여러 DB로 분산 저장)된 데이터베이스에서 일어날 수 있는 상황이다. A와 B가 주고 받는 대화에서 A의 정보는 파티션A에 저장되고 B의 정보는 파티션B에 저장된다고 할때 각 파티션 내에서 복제 지연이 발생할 수 있다. A의 대화1 이후 B의 대화1이 진행되어도 서로 다른 파티션이기 때문에 전역 순서가 없다. 때문에 제 3자가 해당 대화를 읽을 때 파티션B의 팔로워에 데이터가 먼저 쌓였다면 해당 정보를 먼저 읽고 파티션A에 저장된 정보를 읽는다. 즉, 실제 데이터의 순서를 바꿔 읽게 된다.

🤔 인과성이 있는 데이터의 순서 문제
인과성이 있는 데이터의 순서를 보장하기 위해 두 데이터를 동일한 파티션에 기록하게 할 수 있다. 하지만 일부 어플리케이션에서는 효율적이지 않다. 따라서, 인과성을 명시적으로 유지하기 위한 알고리즘을 사용할 수도 있다.(이 부분도 나중에 공부해보자)

🤔 인과성이 있는 데이터를 동일한 파티션에 기록하는 것이 부절절한 어플리케이션?
인과성이 있는 데이터가 너무 많을 경우, 결국 하나의 파티션으로 몰릴 경우 샤딩의 의미가 없어진다.

카테고리:

업데이트:

댓글남기기