System Design

Databases #2 - Data Replication

김여미 2023. 4. 16. 21:49

본 글은 educative.io  Grokking Modern System Design Interview 코스의 Databases 챕터 내용을 정리한 글입니다.

 

데이터는 전체 비즈니스를 이끌어가는 조직의 주요 자산이다. 데이터는 뭐가 중요하고 어떤 변화가 필요한지에 대한 비즈니스적 인사이트를 제공한다. 조직은 또한 클라이언트의 정보를 안전하게 저장하고 서빙해야 한다. 온라인 비즈니스를 성공적으로 운영하려면 다양한 조건에서 필요한 데이터에 제때 액세스 할 수 있어야 한다.

데이터 저장소에는 다음과 같은 특성들이 필요하다. 이는 하나의 노드만으로는 달성하기 어렵다.

  • 장애시 가용성 - 일부 디스크, 노드, 네트워크 및 전력 중지
  • 확장성 - 읽기, 쓰기 및 기타 작업 증가
  • 성능 - 클라이언트의 짧은 대기시간 및 높은 처리량

Replication

복제는 가용성, 확장성, 성능을 확보하기 위해 다양한 노드에 여러개의 데이터 복사본을 저장하는 것을 뜻한다. 이번 챕터에서는 단일 노드가 전체 데이터를 저장하는데 충분하다고 가정한다.

복제는 가용성과 같은 이점들이 있지만 복잡하기도 하다. 복제된 데이터를 자주 변경할 필요가 없는 경우는 간단하지만 복제된 데이텅의 변경사항을 시간이 지남에 따라 유지해야 할 때 문제가 발생한다.

복제로 인해 다음과 같은 복잡성이 추가될 수 있다. 관련 내용을 이번 챕터에서 다룰 것이다.

  • 여러 복제본 사이의 데이터 일관성을 어떻게 유지할 것인지?
  • 장애난 복제 노드를 어떻게 처리할 것인지?
  • 복제를 동기방식으로 할것인지? 비동기식으로 할 것인지?
    • 비동기식 복제의 경우 복제 지연을 어떻게 처리할지?
  • 동시 쓰기를 어떻게 처리할지?
  • 사용하는 프로그래머에게 노출되어야 하는 일관성 모델은 어떤것이어야 하는지?

Synchronous versus asynchronous replication

복제본 노드에 변경사항을 전파하는 두가지 방법으로 동기식 복제와 비동기식 복제가 있다.

동기식 복제에서는 primary 노드가 secondary 노드로부터 데이터 업데이트와 관련한 ack 를 받기를 기다린다. 모든 secondary 노드로부터 ack 를 받으면 primary 노드는 클라이언트에게 성공을 보고한다. 비동기식 복제에서는 primary 노드가 ack 를 기다리지 않고 자체 업데이트한 후 클라이언트에게 성공을 보고한다.

동기식 복제의 장점은 모든 secondary 노드가 primary 노드의 최신 상태를 유지한다는 것이다. 그러나 이 방식의 단점은 네트워크 오류 또는 장애시 secondary 노드로부터 ack 를 받을 수 없게되면 primary 노드에서 클라이언트로의 응답시간이 지연될 수 있다.

반대로 비동기식 복제의 장점은 secondary 노드가 다 내려가더라도 primary 노드는 작업을 계속할 수 있다는 것이다. 그러나 primary 노드 실패시 secondary 노드로 복제되는 쓰기가 유실될 수 있다.

아래 그림은 각 복제 방식에서 데이터 일관성과 가용성 사이의 trade off 를 설명한 그림이다.

Data replication models

이 섹션에서는 다음 모델의 장단점에 대해 설명한다.

  • 단일 리더 또는 Primary-Secondary 복제
  • 다중 리더 복제
  • Peer-topeer 또는 리더가 없는 복제

Single leader/primary-secondary replication

primary-secondary 복제에서 데이터는 여러 노드로 복제된다. 하나의 노드가 primary 로 지정되며, 클러스터에 대한 모든 쓰기 작업을 처리한다. 또한 모든 쓰기를 secondary 노드로 전파하고 동기화 상태를 유지한다.

이 복제 방식은 읽기 작업이 많은 워크로드에 적합하다. 증가하는 읽기 요청으로 확장을 할 때 팔로워를 추가하고 읽기 부하를 가용한 팔로워들에게 분산시킬 수 있다. 그러나 많은 팔로워들에게 데이터를 복제하는 것은 primary 노드의 병목현상을 발생시킬 수 있다. 또한 쓰기 작업이 많은 경우에는 부적절한다.

다른 장점으로는 읽기 복원력이 뛰어나다는 것이다. secondary 노드는 primary 노드에 장애가 발생한 경우에도 읽기 요청을 처리할 수 있다.

비동기식 복제를 사용하는 경우 이 방식은 일관성이 없다. 서로 다른 복제본에서 데이터를 읽어온 클라이언트는 데이터에 일관성이 없음을 발견할 수 없다. priamry 노드에 장애가 발생하여 secondary 노드로 데이터 업데이트를 전파하지 못하는 경우가 그러하며, 이렇게 전달되지 않은 업데이트는 유실될 수 있다.

Question

: primary 노드가 장애나면 어떤 일이 일어날까?

→ primary 노드 장애 발생시 secondary 노드를 primary 로 지정하여 복구 프로세스의 속도를 빠르게 할 수 있다. 새로운 primary 노드를 선택하는 방식에는 수동 또는 자동이 있다.

수동 접근 방식에서는 운영자가 primary 노드를 결정하고 다른 모든 secondary 노드에게 알린다.

자동 접근 방식에서는 secondary 노드가 primary 노드에 장애가 발생한 것을 발견하면 leader election 을 통해 새 primary 노드를 결정한다.

Primary-secondary replication methods

primary-secondary 복제에는 다양한 복제 방법이 있다.

  • Statement-based replication
    • 이 방식에서 primary 노드는 해당 노드가 수행한 모든 statement 를 저장하고, secondary 노드에 전달하여 이를 수행하게 한다. 이 방식은 MySQL 5.1 이하 버전에서 사용되었다.
    • 이 방식은 괜찮아보이지만 비결정적인 함수 (NOW() 와 같은) 를 사용하면 팔로워와 리더에 대해 별개의 쓰기를 발생시킬 수 있다는 단점이 있다. 또한 특정 쓰기 명령문이 이전 쓰기에 의존하고, 두개의 명령문이 팔로워에 서로 다른 순서로 전달된 경우, 팔로워 노드의 결과는 비결정적이다.
  • Write-ahead log (WAL) shipping
    • 이 방식에서는 primary 가 쿼리를 실행하기 전에 미리 쓰기 로그 파일에 쿼리를 저장한다. 그런 다음 이 로그를 사용하여 secondary 노드에 데이터를 복사한다. 이 방식은 PostgreSQL 과 Oracle 에서 사용한다. WAL 의 문제는 매우 낮은 수준의 데이터만 정의할 수 있다는 것이다. DB 엔진의 내부 구조와 강하게 결합되어있어서 리더와 팔로워의 소프트웨어 업그레이드를 복잡하게 만든다.
  • Logical (row-based) log replication
    • 이 방식에서는 모든 보조노드가 실제 데이터 변경사항을 복제한다. 예를들어 테이블에 행이 삽입되거나 삭제된 경우 secondary 는 해당 특정 테이블에서 변경 내용을 복제한다. binary log 는 primary 노드에서의 DB 테이블 변경사항을 레코드 레벨로 기록한다. primary 의 복제본을 만들기 위해 secondary 노드는 이 데이터를 읽고 이에 따라 레코드를 변경한다. 행 기반 복제는 DB 엔진 내부의 데이터 레이아웃에 대한 정보가 필요하지 않기 때문에 WAL 과 같은 문제는 없다.

Multi-leader replication

위와 같이 비동기 복제를 사용하는 단일 리더 복제는 단점이 있다. primary 노드가 하나뿐이며, 모든 쓰기는 이를 거쳐야해서 성능이 제한된다. primary 노드에 오류가 발생하면 secondary 노드에 업데이트가 안될 수 있다.

다중 리더 복제는 단일 리더 복제의 대안이며, primary 노드가 여러개 존재한다. 이러한 유형의 복제는 MySQL 에서 Tungsten Replicator와 같은 외부 도구와 함께 사용된다.

이러한 종류의 복제는 오프라인 상태에서도 작업을 계속할 수 있는 앱 (예를들어 오프라인에서도 사용 가능한 일정 관리 앱 등) 에서 매우 유용하다. 온라인 상태가 되면 로컬 DB (primary 노드 역할을 하는) 에서 다른 노드로 변경 내용을 복제할 수 있다.

Conflict

다중 리더 복제의 상당한 단점은 모든 primary 노드가 쓰기 요청을 동시에 처리하기 때문에 두 노드간 충돌이 발생할 수 있다는 점이다. 동일한 데이터가 두 클라이언트에 의해 동시에 편집된다고 가정했을 때, 연결된 primary 노드에서 쓰기 작업이 성공하지만 비동기로 다른 primary 노드에 이 쓰기 작업이 전달되면 충돌이 발생한다.

Handle conflicts

충돌은 서로 다른 노드에 다른 데이터를 발생시킬 수 있다. 데이터를 유실 없이 효율적으로 처리해야 한다. 아래는 몇가지 충돌을 해결하는 방식들이다.

  • Conflict avoidance
    • 애초에 충돌이 발생하지 않도록 하는 방식이다. 동일한 레코드에 대한 모든 쓰기가 동일한 리더로 전달되는지 검증할 수 있는 경우 충돌을 회피할 수 있다.
    • 그러나 사용자가 지역을 옮겨서 다른 데이터센터에 더 가까워지는 경우 여전히 충돌이 발생할 수 있다. 이런 경우 트래픽 경로를 다시 변경할 필요가 있다. 또한 충돌 회피 접근 방식이 실패하고 동시 쓰기가 발생할 수 있다.
  • Last-write-wins
    • 모든 노드는 로컬 시계를 사용하여 각 업데이트에 타임스탬프를 할당한다. 충돌이 발생하면 가장 최근 타임스탬프를 가진 업데이트가 선택된다.
    • 이 접근 방식 또한 분산 시스템에서 클럭 동기화가 어렵다는 한계점이 있다. clock skew 로 인해 데이터 유실이 발생할 수 있다.
  • Custom logic
    • 이 방식에서는 앱의 필요에 따라 충돌을 처리하는 자체 로직을 작성할 수 있다. 이 로직은 읽기와 쓰기 모두에서 실행될 수 있으며, 시스템이 충돌을 감지하면 커스텀 충돌 처리기를 호출한다.

Multi-leader replication topologies

다중 리더 복제가 구현될 수 있는 토폴로지로 circular, star, all-to-all 토폴로지 등 다양한 종류가 있다. 가장 일반적인 것은 all-to-all 토폴로지이다. star 또는 circular 의 경우 하나의 노드가 장애나면 전체 시스템에 영향을 줄 수 있다는 비슷한 단점이 있기 때문이다.

Peer-to-peer/leaderless replication

primary-secondary 복제에서 primary 노드는 병목과 SPOF 이다. 또한 읽기 확장성을 확보할 수는 있지만 쓰기 확장성을 제공할 수는 없다. peer-to-peer 복제 모델은 primary 노드를 가지지 않게 함으로써 이러한 문제를 해결한다. 모든 노드는 동일한 가중치를 가지며, 읽기 및 쓰기 요청을 받을 수 있다. 아마존 DynamoDB 에서 이러한 방식이 쓰였다.

primary-secondary 복제와 같이 이 복제도 일관성이 없을 수 있다. 여러 노드가 쓰기 요청을 처리하면 동시 쓰기가 발생할 수 있기 때문이다. write-write inconsistency 를 해결하는데 유용한 방법으로 quorum 이 있다.

Quorums

세개의 노드가 있다고 가정해보자. 적어도 두 개의 노드는 성공적인 업데이트 결과를 반환함이 보장된다면, 나머지 하나의 노드만 장애가 발생했음을 의미한다. 이는 두개의 노드에서 읽으면 적어도 하나의 노드는 업데이트 된 버전을 가지고 있다는 것이고, 시스템은 계속해서 동작할 수 있다.

만약 n 개의 노드를 가지고 있다면 모든 쓰기는 적어도 w 개의 노드에 업데이트 되어야 성공했다고 간주할 수 있고, 또한 r 개의 노드로부터 읽기를 해야한다. 우리는 w + r > n 인 경우에 업데이트된 값을 얻을 수 있는데, 적어도 읽기를 수행할 수 있는 노드 중 적어도 한 노드는 업데이트된 쓰기를 가지고 있어야 하기 때문이다. 쿼럼 읽기와 쓰기는 이러한 r, w 값을 준수한다. dynamo 스타일의 DB 는 이러한 n, r, w 값이 설정 가능하다.