3 minute read

최종 일관성 데이터베이스는 서로 다른 두 복제본에 동시에 동일한 질문을 하면 두 개의 다른 답변을 얻을 수 있다. 데이터베이스에 복제본이 하나만 있는 것처럼 보이도록 하는 모델을 선형화 가능성이라고 한다.

선형화 가능성의 기본 개념은 시스템이 데이터의 복사본이 하나만 있고 모든 작업이 원자적인 것처럼 보이게 만드는 것이다. 이 보장을 통해 실제로 여러 개의 복제본이 있을 수 있지만 애플리케이션은 이에 대해 걱정할 필요가 없다.

선형화 가능 시스템에서는 한 클라이언트가 쓰기를 성공적으로 완료하는 즉시 데이터베이스에서 읽는 모든 클라이언트가 방금 쓰여진 값을 볼 수 있어야 한다. 데이터의 단일 복사본이라는 착각을 유지한다는 것은 읽은 값이 가장 최근의 최신 값이며 오래된 캐시나 복제본에서 나온 것이 아니라는 것을 보장하는 것을 의미한다. 다시 말해, 선형화 가능성은 최신성을 보장하는 것이다.

선형화 가능성에 의존하기

선형화 가능성은 어떤 상황에서 유용할까?

잠금 및 리더 선출

단일 리더 복제를 사용하는 시스템에서는 리더가 여러 명이 아니라 실제로 한 명만 존재하도록 해야 한다(두뇌 분할). 리더를 선출하는 한 가지 방법은 잠금을 사용하는 것이다. 시작하는 모든 노드는 잠금을 획득하려고 시도하고, 성공한 노드가 리더가 된다. 이 잠금을 어떻게 구현하든 선형화할 수 있어야 한다.

분산화된 잠금과 리더 선출을 구현하기 위해 Apache ZooKeeper, etcd와 같은 코디네이터 서비스가 자주 사용된다. 이들은 합의 알고리즘을 사용해 선형화 가능한 연산을 내결함성 방식으로 구현한다. 분산 잠금은 Oracle RAC(Real Application Clusters)와 같은 일부 분산 데이터베이스에서 더 세분화된 수준에서 사용된다. RAC는 디스크 페이지당 잠금을 사용하며, 여러 노드가 동일한 디스크 스토리지 시스템에 대한 액세스를 공유한다. 이러한 선형화 가능한 잠금은 트랜잭션 실행의 중요한 경로에 있기 때문에 RAC를 배포할 때는 데이터베이스 노드 간의 통신을 위한 전용 클러스터 상호 연결 네트워크가 존재한다.

제약 조건 및 고유성 보장

사용자 이름이나 이메일 주소는 한 사용자를 고유하게 식별해야 하며, 파일 스토리지 서비스에서는 경로와 파일명이 같은 파일이 두 개 있을 수 없다. 두 사람이 동시에 같은 이름의 사용자나 파일을 만들려고 하면 그 중 한 명에게 오류가 반환되도록하는 선형화 기능이 필요하다. 사용자가 서비스에 등록할 때 선택한 사용자 이름에 ‘잠금’이 걸린다. 은행 계좌 잔액이 마이너스가 되지 않도록 하거나, 창고에 재고보다 많은 상품을 판매하지 않도록 하거나, 비행기나 극장에서 두 사람이 동시에 같은 좌석을 예약하지 않도록 하려는 경우에도 비슷한 문제가 발생할 수 있다. 이러한 제약 조건은 모두 모든 노드가 동의하는 하나의 최신 값(계정 잔액, 재고 수준, 좌석 점유)이 있어야 한다.

채널 간 타이밍 종속성

사용자가 사진을 업로드할 수 있는 웹사이트가 있고 백그라운드 프로세스가 더 빠른 다운로드를 위해 사진의 크기를 저해상도로 조정한다고 가정해 보자. 이미지 리사이저에 크기 조정 작업을 수행하도록 명시적으로 지시해야 하며, 이 지시는 메시지 큐를 통해 웹 서버에서 리사이저로 전송된다. 사진이 먼저 파일 저장 서비스에 쓰여지고 쓰기가 완료되면 리사이저에 대한 명령이 대기열에 배치된다.

파일 스토리지 서비스가 선형화 가능하다면 이 시스템은 정상적으로 작동한다. 선형화할 수 없다면, 메시지 큐가 스토리지 서비스 내부의 내부 복제보다 빠를 수 있는 경쟁 조건이 발생할 위험이 있다. 이 경우 리사이저가 이미지를 가져올 때, 이전 버전의 이미지가 표시되거나 아무것도 표시되지 않을 수 있다.

이 문제는 웹 서버와 리사이저 사이에 파일 저장소와 메시지 대기열이라는 두 가지 다른 통신 채널이 있기 때문에 발생한다. 선형화 가능성에 대한 최신성이 보장되지 않으면 이 두 채널 간에 경합 조건이 발생할 수 있다.

선형화 가능한 시스템 구현하기

선형화 가능성은 본질적으로 “데이터의 복사본이 하나만 있고 데이터에 대한 모든 연산이 원자적인 것처럼 동작한다”는 의미다. 실제로 데이터의 복사본 하나만 사용하는 것이 선형화 가능성을 구현하는 것이다. 그러나 이러한 접근 방식은 결함을 버틸 수 없다. 해당 복사본을 보유한 노드에 장애가 발생하면 데이터가 손실되거나 적어도 노드를 다시 불러올 때까지는 액세스할 수 없다.

내결함성 시스템을 만들기 위한 가장 일반적인 접근 방식은 복제를 사용하는 것이다.

단일 리더 복제(잠재적으로 선형화 가능)

단일 리더 복제를 사용하는 시스템에서는 리더가 쓰기에 사용되는 데이터의 기본 복사본을 가지고 있고 팔로워는 다른 노드에 데이터의 백업 복사본을 유지한다. 리더 또는 동기적으로 업데이트되는 팔로워에서 읽기를 수행한다고 해서 선형화할 수 있는 것은 아니다.

리더를 읽기에 사용하려면 리더가 누구인지 확실히 알고 있다는 가정이 전제되어야 한다. “진실은 다수에 의해 정의된다”는 말처럼, 노드가 자신이 리더라고 생각할 수 있지만 실제로는 그렇지 않을 수 있으며, 망상적 리더가 계속 요청을 처리하면 선형성을 위반할 가능성이 높다. 비동기 복제를 사용하면 장애 조치로 인해 전송된 쓰기가 손실될 수도 있으며, 이는 내구성과 선형성을 모두 위반하는 것이다.

합의 알고리즘(선형화 가능)

일부 합의 알고리즘은 단일 리더 복제와 유사하다. 그러나 합의 프로토콜에는 두뇌 분할과 오래된 복제본을 방지하기 위한 조치가 포함되어 있다. 이러한 세부 사항 덕분에 컨센서스 알고리즘은 선형화 가능한 스토리지를 안전하게 구현할 수 있다. 예를 들어 ZooKeeper와 etcd가 이러한 방식으로 작동한다.

다중 리더 복제(선형화 불가)

다중 리더 복제를 사용하는 시스템은 여러 노드에서 동시에 쓰기를 처리하고 다른 노드에 비동기적으로 복제하기 때문에 선형화할 수 없다. 따라서 쓰기 충돌이 발생할 수 있다. 이러한 충돌은 데이터의 단일 복사본이 없기 때문에 발생하는 현상이다.

리더 없는 복제(선형화할 수 없음)

리더 없는 복제를 사용하는 시스템의 경우, 쿼럼 읽기 및 쓰기를 요구함으로써 “강력한 일관성”을 얻을 수 있다고 주장하는 사람들이 있다. 하지만 이는 사실이 아니다.

시계 타임스탬프가 시계 왜곡으로 인해 실제 이벤트 순서와 일치한다고 보장할 수 없기 때문에 시간대별 시계를 기반으로 하는 “마지막 쓰기가 승리하는” 충돌 해결 방법은 거의 비선형화할 수 없다. 엉성한 쿼럼도 선형화 가능성을 망친다.