책너두 (데이터 중심 애플리케이션 설계) 15일차 (~183p)

요약

  • 다중 리더 복제에서 쓰기 충돌을 다루는 방법을 이해하게 됨.
    • 동기식과 비동기식 충돌 감지 방법이 있음.
      • 동기로 충돌 감지 만들 수 있지만, 다중 리더 복제 장점을 잃음
      • 비동기로 감지하면 사용자에게 충돌을 해소하게끔 요청해야 하고, 이미 너무 늦음
    • 충돌 회피하는 방식도 있음.
      • 동일한 리더를 거치게 함.
      • 얘도 사실상 단일 리더랑 다를게 없음.
    • 일관된 상태로 수렴하게 한다.
      • 수렴을 위한 다양한 방법을 사용
    • 사용자 정의로 충돌 해소 로직 작성할 수 있음
      • 읽기, 쓰기 시점에 코드 실행함
  • 다중 리더 복제 토폴로지에 대한 내용을 이해함.
    • 노드와 노드간 통신경로를 의미함
    • 데이터베이스 마다 토폴로지가 다름
    • 토폴로지별 장단점이 있음.
    • 전체 연결 토폴로지가 내결함성이 좋지만, 데이터 일관순서가 보장되지 않을 수 있음
      • 버전 벡터 방식으로 처리가 가능하다고 함.
  • 리더 없는 복제에 대한 내용을 이해함
    • 이런 종류의 데이터베이스를 다이나모 스타일이라고 함.
    • 리더 없는 복제에서 노드가 다운됐을 때 데이터베이스에 쓰기 방식을 이해함
      • 한 복제 서버가 다운되도 다른 복제 서버가 온라인이라면 장애 복구 없이 쓰기를 허용함.
        • 누락된 메시지는 읽기 시점이나 안티 엔트로피 방식을 사용한다.
        • 쓰기 허용 범위는 읽기와 쓰기를 위한 정족수 값(w, r, n)으로 결정된다.

메모

쓰기 충돌 다루기

  • 다중 리더 복제의 제일 큰 문제는 쓰기 충돌이 발생한다는 점임.
  • 사용자 1이 페이지 제목을 A → B 로 변경하고, 같은 시각에 사용자 2가 페이지 제목을 A → C 로 변경한 상황이 있다고 가정하자.
    • 각 사용자의 변경은 로컬 리더에 성공적으로 적용됨.
    • 하지만 변경을 비동기로 복제할 때, 충돌을 감지함
    • 이 문제는 단일 리더 데이터베이스에서는 일어나지 않음.

동기 대 비동기 충돌 감지

  • 단일 리더 데이터베이스는 첫 번째 쓰기가 완료될 때까지 두 번째 쓰기를 차단하거나 두 번째 쓰기 트랜잭션을 중단해서 사용자가 쓰기를 재시도하게 함.
  • 다중 리더 설정에서는 두 쓰기는 모두 성공하며 충돌은 이후 특정 시점에서 비동기로만 감지함.
    • 사용자에게 충돌을 해소하게끔 요청하면 너무 늦을 수 있음.
  • 충돌 감지는 동기식으로 만들 수 있음.
    • 즉, 쓰기가 성공한 사실을 사용자에게 말하기 전에 모든 복제 서버가 쓰기를 복제하기를 기다림.
    • 그러나 이 방식은 각 복제 서버가 독립적으로 쓰기를 허용하지 못하므로 다중 리더 복제의 장점을 잃음.

충돌 회피

  • 충돌 처리의 간단한 전략은 충돌을 피하는 것임.
  • 많은 다중 리더 복제 구현 사례에서, 충돌을 잘 처리하지 못함.
    • 따라서, 충돌을 피하는 것은 자주 권장되는 방법임.
  • 특정 레코드의 모든 쓰기를 동일한 리더를 거치도록 애플리케이션이 보장하게 만들어서 충돌이 발생하지 않게 만듦.
    • 애플리케이션에서 특정 사용자의 요청을 동일한 데이터센터로 항상 라우팅하게 해서, 데이터센터 내 리더에 대해 읽기, 쓰기하도록 보장함.
    • 사실상 단일 리더로 사용하게끔 할 수 있음.
  • 만약, 데이터센터가 고장나서 트래픽을 다른 데이터센터로 다시 라우팅하거나, 사용자가 다른 지역으로 이동하여 다른 데이터센터로 이동하고 싶다면 지정된 리더를 변경할 수 있음.
    • 이 경우 충돌 회피가 실패함.
    • 변경할 리더에서 동시 기록 처리 가능성을 대처해야 함.

일관된 상태 수렴

  • 다중 리더 설정에서 쓰기 순서가 정해져 있지 않아 최종 값이 무엇인지 명확하지 않음.
    • 리더 1 에서 A → B 로 변경 / 리더 2 에서 A → C 로 변경
      • 리더 1 입장에서 최종 값은 B, 리더 2 입장에서 최종 값은 C
      • 최종값이 B인지 C인지 알 수 없음.
  • 모든 복제 계획은 최종적으로 동일하다는 사실을 보장해야 함.
    • 데이터베이스는 수렴(convergent)방식으로 충돌을 해소해야 함.
    • 수렴 충돌 방법은 다양함
      • 각 쓰기에 고유 ID를 부여하고 가장 높은 ID를 가진 쓰기를 고른다.
        • 다른 쓰기는 버린다.
        • 타임스탬프를 사용한다면, 최종 쓰기 승리(last write wins, LWW)라고 함.
        • 이 방법은 대중적이지만 데이터 유실 위험이 있음.
      • 각 복제 서버에 고유 ID를 부여하고, 높은 숫자의 복제 서버에서 생긴 쓰기가 낮은 순서의 복제 서버에서 생긴 쓰기보다 항상 우선적으로 적용됨
        • 이 방식도 데이터 유실 가능성이 있음.
      • 어떻게든 값을 병합한다.
        • ex) 사전 순으로 정렬한 후 연결한다.
      • 명시적 데이터 구조에 충돌을 기록해 모든 정보를 보존한다.
        • 나중에(사용자에게 메시지 보여줌) 충돌을 해소하는 애플리케이션 코드를 작성한다.

사용자 정의 충돌 해소 로직

  • 충돌 해소에 가장 적합한 방법은 애플리케이션에 따라 다름
  • 대부분의 다중 리더 복제 도구는 애플리케이션 코드를 사용해서 충돌 해소 로직을 작성함.
  • 위 로직은 쓰기 혹은 읽기 수행 중에 실행됨.
    • 쓰기 수행 중
      • 복제된 변경 사항 로그에서 데이터베이스 시스템 충돌을 감지하자마자 충돌 핸들러를 발생함.
      • 이 핸들러는 일반적으로 사용자에게 충돌 내용을 표시하지 않음.
      • 백그라운드 프로세스에서 빠르게 실행돼야 함.
    • 읽기 수행 중
      • 충돌을 감지하면 모든 충돌 쓰기를 저장함.
      • 다음 번 데이터를 읽을 때, 여러 버전의 데이터가 애플리케이션에 반환됨.
        • 애플리케이션은 사용자에게 충돌 내용을 보여주거나 자동으로 충돌을 해소할 수 있음.
          • 충돌 해소 결과는 다시 데이터베이스에 기록함.
          • 카우치 DB가 위 방식으로 동작함.
  • 충돌 해소는 보통 전체 트랜잭션이 아니라 개별 로우나 문서 수준에서 적용됨

자동 충돌 해소

동시 데이터 수정할 때 발생하는 충돌을 자동으로 해소하는 연구가 있음.
이를 사용하면 애플리케이션이 다루는 다중 리더 데이터 동기화를 훨씬 단순하게 만듦.

  • 충돌 없는 복제 데이터 타입
    ex) 셋(set), 맵(map), 정렬 목록, 카운터 → 데이터 구조 집합으로 동시에 여러 사용자 편집이 가능하며, 합리적 방법으로 충돌을 자동 해소함.
  • 병합 가능한 영속 데이터 구조
    깃 버전 제어 시스템과 유사하게 명시적으로 히스토리를 추적하고, 삼중 병합 함수를 사용함.
  • 운영 변환
    협업 편집 애플리케이션의 충돌 해소 알고리즘임.

 

충돌은 무엇인가?

  • A→B / A→C 와 같은 충돌 의 종류는 명백함
  • 회의실 예약은 충돌 감지가 조금 더 어려움
    • 같은 시간에 같은 회의실을 예약하는 두 개의 다른 예약이 생기면 충돌이 발생함.
    • 7장에서 충돌 예제 살펴본 다음, 12장에서 복제 시스템에서 충돌 감지 해소 & 확장 접근 방식을 살펴본다.

다중 리더 복제 토폴로지

  • 복제 토폴로지는 쓰기를 한 노드에서 다른 노드로 전달하는 통신 경로를 설명함.
    • 두 리더가 있다면, 가능한 토폴로지는 하나뿐임.
      • 리더 1은 모든쓰기를 리더 2로 전송해야하고, 그 반대도 마찬가지임.
    • 리더가 둘 이상이면 다양한 토폴로지가 가능함.

  • 가장 일반적인 토폴로지는 전체 연결(all-to-all)임. (그림의 c)
    • 모든 리더가 각자의 쓰기를 다른 모든 리더에서 전송함.
  • 전체 연결보다 제한된 토폴로지도 사용됨
    • MySQL은 기본적으로 원형 토폴로지만 제공함.
  • 또 다른 대중적인 토폴로지로 별 모양 토폴로지가 있음.
    • 지전된 루트 노드 하나가 다른 모든 노드에 쓰기를 전달함.
  • 원형과 별 모양 토폴로지에서 쓰기는 모든 복제 서버에 도달하기 전, 여러 노드를 거쳐야 함.
    • 따라서, 노드들은 다른 노드로 부터 받은 데이터 변경 사항을 전달해야 함.
    • 무한 복제 루프를 방지하기 위해 각 노드에 고유 식별자가 있음.
      • 다른 노드로부터 데이터 변경사항을 받았는데, 자신의 식별자가 태깅된다면 이미 처리됐기에 이 데이터 변경사항은 무시됨
    • 원형, 별 토폴로지의 문제점은 하나의 노드에 장애가 발생하면 장애가 다른 노드 간 복제 메시지 흐름에 방해를 줌.
      • 즉, 해당 노드가 복구될 떄까지 통신할 수 없음.
      • 토폴로지는 장애 노드를 회피하게끔 재설정할 수 있음.
        • 하지만, 대부분 배포에서 이런 재설정은 수동으로 수행해야 함.
        • 메시지가 여러 경로를 따라 이동할 수 있으면 단일 장애점을 피할 수 있기에 좀 더 빽빽하게 연결된 토폴로지의 내결함성이 훨씬 더 좋음.

 

  • 전체 연결 토폴로지도 문제가 있음. 

  • 일부 네트워크 연결이 다른 연결보다 빠르다면 일부 복제 메시지가 다른 메시지를 “추월”할 수 있음.
    • 클라이언트 A 가 리더 1 에 A 로우를 삽입함.
    • 클라이언트 B 가 리더 3 에 A 로우를 갱신함.
    • 리더 2에서 삽입이 먼저와야 하는데, 갱신이 먼저 도착함
  • 위 문제는 “일관된 순서 읽기” 가 되지 않은 인과성 문제임.
    • 위 문제는 단순히 타임스탬프를 추가하는 방식으로 충분하지 않음.
    • 삽입, 갱신하는 이벤트들이 각 노드에서 시간이 동기화됐다고 신뢰할 수 없기 때문(8장 참고)
  • 위 이벤트를 올바르게 정렬하기 위해 버전 벡터(version vector) 기법을 사용할 수 있음.
    • p186에서 설명
    • 많은 다중 리더 복제 시스템에서 충돌 감지 기법은 제대로 구현되지 않음.
      • ex) 포스트그레스큐엘, MySQL
    • 데이터베이스를 철저히 테스트 해서 실제로 믿을 만한 보장을 제공하는지 확인하는 편이 좋다.

리더 없는 복제

  • 일부 데이터 저장소 시스템은 리더의 개념을 버리고 모든 복제 서버가 클라이언트로부터 쓰기를 직접 받을 수 있게 허용하는 접근 방식을 사용함.
    • 리더 없는 복제는 아마존이 내부 다이나모(Dynamo) 시스템에서 사용한 후, 다시 데이터베이스용 아키텍처로 유행함.
      • ex)리악, 카산드라, 볼드모트 → 다이나모에서 영감을 얻은 리더 없는 복제 모델의 오픈 소스 데이터스토어임.
      • 이런 종류의 데이터베이스를 다이나모 스타일이라고 함.
    • 클라이언트가 여러 복제 서버에 쓰기를 직접 전송하기도 하고, 코디네이터 노드가 클라이언트를 대신해서 쓰기를 수행하기도 함.
      • 리더 데이터베이스와 달리 코디네이터 노드는 특정 순서로 쓰기를 수행하지 않음.

노드가 다운됐을 때 데이터베이스에 쓰기

  • ex) 3개의 복제 서버를 가진 데이터베이스가 있고, 이 중 한개를 사용할 수 없는 상황이라고 가정하자.
    • 리더 기반 설정에서 쓰기 처리를 하려면 팔로워 장애 복구를 해야 함.
    • 리더 없는 설정에는 장애 복구가 필요하지 않음.
      • ex) 클라이언트 A 가 쓰기를 3 개의 모든 복제 서버로 병렬로 전송함.
      • 두 개의 복제 서버만 쓰기를 받을 수 있는데, 이것만으로 충분하다고 가정하자.
      • 클라이언트는 두 개의 ok 응답을 받은 후 쓰기가 성공한 것으로 간주함.
        • 하나의 쓰기를 놓친 사실은 단순히 무시한다.
      • 만약, 장애가 발생한 복제 서버가 다시 on line 이 되면, 클라이언트가 다시 데이터를 조회할 때 오래된 값을 얻을 수 있음.
        • 이를 해결하기 위해 읽기 요청을 병렬로 여러 노드에 전송한다.
        • 버전 숫자를 사용해서 어떤 값이 최신 내용인지 결정한다. (p186 동시 쓰기 감지 참고)

읽기 복구와 안티 엔트로피

  • 사용 불가능한 노드가 온라인 상태가 된 후 누락된 쓰기는 어떻게 처리하는가?
  • 다이나모 스타일 데이터스토어는 두 가지 메커니즘을 주로 사용함.
    • 읽기 복구
      • 위에서 설명한 것처럼 오래된 값을 조회하면, 그 값의 버전을 확인하여 예전 값이란 걸 알기에 해당 복제 서버에 새로운 값을 다시 기록한다.
      • 이 접근 방식은 값을 자주 읽는 상황에 적합함.
    • 안티 엔트로피
      • 일부 데이터스토어에 백그라운드 프로세스를 두고 복제 서버 간 데이터 차이를 지속적으로 찾아서 누락된 데이터를 하나의 복제 서버에서 다른 서버로 복사한다.
      • 리더 기반 복제에서의 복제 로그와 달리, 안티 엔트로피 처리는 특정 순서로 쓰기를 복사하므로 데이터 복사되기까지 상당한 지연이 있을 수 있음.

읽기와 쓰기를 위한 정족수

  • 위 예제에서는 3개의 복제 서버 중 두 개에서만 ok 를 받아도 성공한 것으로 간주했는데, 이것의 범위를 어디까지 허용해야하는가?
  • 일반화해보자.
    • n개의 복제서버가 있을 때, 모든 쓰기는 w개의 노드에서 성공해야 쓰기가 확정되고, 모든 읽기는 최소 r개의 노드에 질의 해야 함. (위 예에서, n=3, w=2, r=2 임)
    • w+r > n 이여야 읽을 때 최신 값을 얻을 것으로 기대함.
      • 최소한의 r 개의 노드 중 하나에서 최신값을 읽을 수 있기 때문
    • 여기서 r과 w를 따르는 읽기와 쓰기를 정족수 읽기와 쓰기라고 부름.
      • 엄격한, 느슨한 정족수로 구분될 수 있음.
    • 다이나모 스타일 데이터베이스에서 n, w, r은 파라미터로 설정 가능함.
      • 일반적으로 n 은 홀수(3, 5)로 하고 w = r = (n + 1) / 2 (반올림) 으로 설정함.
      • 상황에 따라 숫자가 달라질 수 있음.
        • ex) 쓰기가 적고 읽기가 많은 작업 부하는 w = n, r = 1로 설정하면 좋음.
          • 이렇게 하면 읽기는 더 빨라지지만, 노드 하나가 고장나면 모든 데이터베이스 쓰기가 실패하는 단점이 있음.

클러스터에는 n개 이상의 노드가 있을 수 있지만 주어진 값은 n개 노드에만 저장된다. → 데이터셋을 파티션해서 한 노드에 들어갈 수 있는 양보다 큰 데이터셋을 지원할 수 있게 한다. (6장 참고)

  • 정족수 조건이 w + r > n 이면, 다음과 같이 사용 불가능한 노드를 용인할 수 있음.
    • w < n 이면 노드 하나를 사용할 수 없어도 쓰기 처리 가능
    • r < n 이면 노드 하나를 사용할 수 없어도 읽기 처리 가능
  • 일반적으로 읽기와 쓰기는 항상 모든 n개의 복제 서버에 병렬로 전송한다.
    • 파라미터 w와 r은 n개의 노드 중 몇 개의 노드에서 성공을 확인해야 하는지를 나타냄.
  • 필요한 w, r 노드보다 사용 가능한 노드가 적다면 쓰기, 읽기는 에러를 반환함.
  • 노드는 다양한 이유로 사용 불가능함.
    • 노드 다운, 작업 실행 오류(ex: 디스크 꽉참), 클라이언트와 노드 간 네트워크 중단 등
    • 노드는 성공 응답을 반환했는지 여부가 중요하고, 오류 종류를 구별할 필요는 없음.

댓글

Designed by JB FACTORY