1. 일관성, 정합성, 무결성 ??
(1) 일관성
- 일관성 : 동일한 시스템 내에서 데이터가 서로 모순되지 않는 것을 의미한다.
- 최종적 일관성(eventual consistency) : 불특정 시간동안 기다리면 결국 모든 읽기 요청이 같은 값을 반환한다는 뜻이다. 복제 데이터베이스는 대부분 최소한 최종적 일관성을 제공한다.
- 이런 약한 보장에도 불구하고 eventual consistency 는 성공적인 애플리케이션과 수익성 있는 비즈니스를 지원한다.
(2) 정합성
- 정합성 : 여러 시스템에서 데이터가 정확하고 일관되게 유지하는 것을 의미한다.
- 일관성과 정합성의 차이점 : 서로 다른 시스템 간의 데이터를 다루는 반면, 데이터 일관성을 동일한 시스템 내의 데이터를 다룬다.
(3) 무결성
- 무결성 : 데이터가 정확하고 유효한 상태를 유지하는 것을 의미한다. 즉, 데이터가 실제 세계를 정확하게 반영하고 오류나 모순이 없는 것을 말한다.
(4) 선형성
- 선형성 : 최신성 보장을 말한다. 선형적 시스템에서 클라이언트가 쓰기를 성공적으로 완료하자마자 그 데이터베이스를 읽는 모든 클라이언트는 방금 쓰여진 값을 볼 수 있어야 한다.
2. 선형성을 보장을 위한 사례
(1) 잠금과 리더 선출
단일 리더 복제를 사용하는 시스템은 리더가 하나만 존재하도록 보장해야 한다. 리더를 선출하는 한 가지 방법은 잠금을 사용하는 것이다. 모든 노드가 시작할 때 잠금 획득을 시도하고 성공한 노드가 리더가 된다.
분산 잠금과 리더 선출을 구현하기 위해 아파치 주키퍼나 etcd 같은 코드네이션 서비스가 종종 사용된다. 이들을 합의 알고리즘을 사용해 선형성 연산을 내결함성이 있는 방식으로 구현한다.
(2) 제약 조건과 유일성 보장
- 제약 조건은 모든 노드가 동의하는 하나의 최신 값(계좌 잔고, 재고 수준, 좌석 점유) 이 있기를 요구한다. 실제 애플리케이션에서는 때때로 이런 제약 조건을 느슨하게 다뤄도 된다. (e.g. 항공편 좌석 오버 부킹)
- 관계형 데이터베이스에서 전형적으로 볼 수 있는 엄격한 유일성 제약 조건은 선형성이 필요하다.
(3) 채널 간 타이밍 의존성
비동기 메시지이 이미지 압축 로직 호출 시점과 이미지 저장 시점 차이에 따라 경쟁 조건의 위험이 존재한다.
메시지 도착(이미지 압축 호출) → 압축할 이미지 저장 : 경쟁 조건 발생
3. 선형성 시스템 구현하기
(1) 복제 방식에 따라 선형성 보장성 비교
- 단일 리더 복제 (선형성 보장 ↑) : 동기식일 경우 선형성 될 가능성이 있고, 비동기 처리와 스냅숏 격리를 사용하면 동시성 버그가 발생하수 있다.
- 합의 알고리즘 (선형성 보장 ↑) : 단일 리더와 비슷하지만 스플릿 브레인과 같은 복제본이 뒤쳐지는 문제를 막을 수단이 포함된다.
- 다중 리더 복제 (선형성 보장 ↓) : 다중 리더 복제 시스템은 충돌 해소가 필요한 충동 쓰기를 만들 수 있다. 여러 노드에서 동시에 쓰기를 처리하고 쓰여진 내용을 비동기로 다른 노드에 복제하기 때문이다.
- 리더 없는 복제 (선형성 보장 ↓) : 정족수 읽기와 쓰기를 요구함으로써 염격한 일관성을 다룰 수 있다고 하지만 정족수 설정과 엄격한 일과넝을 어떻게 정의하느냐에 따라 달라질 수 있다.
(2) 선형성과 정족수
- 정족수 조건이 만족(w + r > n) 됨에도 이 실행은 선형적이지 않다. 이유는 w + r > n 이어도 사용 불가능한 노드를 용인하기 때문이다.
- w < n 이면 노드 하나를 사용할 수 없어도 여전히 쓰기를 처리할 수 있다.
- r < n 이면 노드 하나를 사용할 수 없어도 여전히 읽기를 처리할 수 있다.
n : 복제 서버 w : 쓰기 성공해야 하는 노드 r : 최소한 읽기 성공해야 하는 노드
(3) CAP 정리
CAP 은 분산 환경 시스템에서 일관성(consistency), 가용성(availability), 분단 내성(partition tolerance) 세 개 중 두 개를 고르라는 표현이다. CAP 는 오해의 소지 여지가 있다. 네트워크 결함이 생겼을 때 선형성(일관성)과 가용성 사이에서 선택해야 한다.
4. 인과적 순서가 전체 순서는 아니다.
(1) 선형성 vs 인과성
- 선형성
- 선형적 시스템에서는 연산의 전체 순서를 정할 수 있다.
- 동시성은 타임라인이 갈라졌다 다시 합쳐지는 것을 의미한다. 동시성은 선형성에서만 발생하는 요소이다.
- 인과성
- 인과성이 전체 순서가 아닌 부분 순서를 정의한다는 것이다.
(2) 선형성은 인과적 일관성보다 강하다.
- 선형성 인과성의 상위 개념으로 인과성이 자동으로 보존되도록 보장해준다.
- 많은 경우에 선형성이 필요한 것처럼 보이는 시스템이 사실 진짜로 필요한 것은 인과적 일관성이며 이는 더 효율적으로 구성할 수 있다.
(3) 램포트 타임스탬프
- 램포트 타임스탬프 : (카운터, 노드ID) 의 쌍이다. 두 노드는 때때로 카운터 값이 같을 수 있지만 타임스탬프에 노드ID를 포함시켜서 타임스탬프는 유일하게 된다. 모든 노드와 모드 클라이언트가 지금까지 본 카운터 값 중 최댓값을 추적하고 모든 요청에 그 최댓값을 포함시킨다.
- 노드가 사용자로부터 사용자명 생성 요청을 막 받고 그 요청이 성공해야 하는지 실패해야 하는지 당장 결정해야 할 때는 이 방법으로는 부족하다.
(4) 전체 순서 브로드 캐스트
- 전체 순서 브로드와 원자적 브로드캐스트를 통해 해결해야 하는 문제는 처리량이 단일 리더가 처리할 수 있는 수준을 넘어설 때 시스템을 어떻게 확장할 것인가와 리더에 장애가 발생했을 때 어떻게 장애 복구를 처리할 것인가이다.
- 전체 순서 브로드캐스트는 신뢰성 있는 전달과 전체 순서가 정해진 전달의 안전성 속성이 필요하다. 이는 곧 메시지 전달되는 시점에 그 순서가 고정되어야 한다는 것이다. 후속 메시지가 이미 전달됐다면 노드는 그 순서의 앞에 메시지를 소급적으로 끼워넣는게 허용되지 않는다.
- 전체 순서 브로드캐스트는 비동기식이다. 메시지는 고정된 순서로 신뢰성 있게 전달되도록 보장하지만 언제 메시지가 전달될지는 보장되지 않으므로 선형성 보장을 위해 compare-and-set 연산을 구현해야한다.
- 읽기 일관성을 보장하기 위해 로그를 통한 순차 읽기, 쓰기를 실행할 때 동기식으로 갱신, 최신 로그 메시지의 위치를 질의를 통해 알 수 있다.
5. 분산 트랜잭션과 합의
(1) 합의(consensus) ??
- 합의의 목적은 비공식적으로 여러 노드들이 뭔가에 동의하게 만드는 것이며, 노드가 동의하는 것의 중요한 이유는 리더 선출과 원자적 커밋 등이 있다.
- 리더 선출 : 네트워크 결함으로 인한 스플릿 브레인 유발을 피할 수 있어야 한다.
- 원자적 커밋 : 여러 노드나 파티션에 걸친 트랜잭션을 보장하기 위해서는 모든 노드가 트랜잭션 결과에 동의하게 만들어야 한다.
(2) 단일 노드에서 분산 원자적 커밋으로
- 단일 노드와는 달리 분산 환경에서는 다른 모든 노드들이 모두 커밋될 것이라고 확실할 때만 커밋이 돼야 한다. 어떤 노드가 트랜잭션 커밋하지만 다른 노드는 어보트한다면 노드들이 서로 일관성이 없어지기 때문이다.
- 기본적으로 분산 환경에서 트랜잭션 커밋은 되돌릴 수 없어야 한다.
- 커밋된 트랜잭션의 효과를 나중에 다른 보상 트랜잭션(compensating transaction) 이 취소하는 것은 가능하다.
(3) 2단계 커밋(2PC)
- 2단계 커밋 : 여러 노드에 걸친 원자적 트랜잭션 커밋을 달성하는, 즉 모든 노드가 커밋되거나 모든 노드가 어보트되도록 보장하는 알고리즘이다.
- 일부 DB 에서는 2PC 가 내부적으로 사용되고 XA 트랜잭션(e.g. java - JTA) 를 지원한다.
- 2PC 는 코디네이터와 참여자로 구성되며, 애플리케이션이 커밋할 준비가 되면 코디네이터가 1단계를 시작하고, 각 노드에 준비(1단계) 요청을 보내 커밋 가능한 상태임을 확인하여 커밋(2단계)한다.
- 모든 참여자가 커밋 준비가 되면 2단계에서 커밋 요청을 보내 커밋이 실제로 일어난다.
- 참여자 중 한명도 동의하지 않을 경우, 2단계에서 모든 노드에 어보트 요청을 보낸다.
(4) 2PC 동작 과정
- 애플리케이션이 분산 트랜잭션을 시작하기를 원하면 코디네이터에게 트랜잭션 ID 를 요청한다.
- 애플리케이션은 각 참여자에서 단일 노드 트랜잭션을 시작하고 단일 트랜잭션에 전역적으로 유일한 트랜잭션ID를 붙인다. (모든 읽기와 쓰기는 단일 노드 트랜잭션 중 하나에서 실행된다.)
- 애플리케이션이 커밋 준비가 되면 코디네이터는 모든 참여자에게 전역 트랜잭션 ID 로 태깅된 준비 요청 보낸다.
- 참여자가 준비 요청을 받으면 모든 상황에서 분명히 트랜잭션을 커밋할 수 있는지 확인한다.
- 코디네이터가 모든 준비 요청에 대해 응답을 받았을 때 트랜잭션 커밋 여부를 최종 결정한다. (커밋 포인트)
- 코디네이터의 결정이 디스크에 쓰여지면 모든 참여자에게 커밋이나 어보트 요청이 전송된다. 해당 결정은 강제해야 한다. 도중에 한 참여자가 죽으면 트랜잭션은 그 참여자가 복구될 때 커밋된다.
(5) 코디네이터 장애
- 트랜잭션이 의심스럽다(in doubt) : 코드네이터가 네트워크 장애가 발생하면 참여자는 커밋 여부를 기다릴 수 밖에 없는 상태를 말한다.
- 코디네이터가 커밋을 결정한 후에 장애 발생시, 2PC 가 완료할 수 있는 유일한 방법은 코디네이터가 복구되기를 기다리는 것 뿐이다.
(6) 3단계 커밋
- 2PC 커밋은 블로킹 원자적 프로토콜이라 부른다. 코디네이터 복구를 기다려야 하기 때문이다.
- 3PC 커밋은 2PC의 대안으로 제안되었지만 현실적으로 원자성을 보장하지 못한다.
- 논블로킹 원자적 커밋은 완벽한 장애 감지기가 필요하다. 즉, 노드의 헬스 체크를 구별할 수 있는 신뢰성있는 메커니즘이 필요한 한계가 존재하며, 지연에 따른 타임아웃과 같은 부분은 완벽한 장애 감지기 영역이 아닌 현실적인 문제로 2PC 가 계속 사용되고 있다.
6. 현실의 분산 트랜잭션
- 분산 트랜잭션의 양면성
- 장점 : 분산 환경에서 안전성 보장을 제공한다.
- 단점 : 운영상 문제를 일으키고 성능을 떨어뜨린다. (e.g. 부가 네트워크 왕복, 장애 복구를 위한 디스크 강제 쓰기)
- 분산 트랜잭션 종류 : 데이터베이스 내부 분산 트랜잭션 / 이종 분산 트랜잭션
- 이종 분산 트랜잭션을 위해서는 정확히 한 번 메시지 처리가 가능해야 한다.
(1) XA 트랜잭션
- X/Open XA(eXtend Architecture) 는 이종 기술에 걸친 2PC 를 구현하는 표준이다.
- XA 는 RDB 계열 뿐만 아니라 Message Broker(e.g. RabbitMQ) 에서 지원된다.
- 코디네이터의 로그는 애플리케이션 서버의 로컬 디스크에 있으므로 그 서버는 재시작돼야 하고 코디네이터 라이브러리가 그 로그를 읽어서 트랜잭션의 커밋/어보트 결과를 복구해야 한다.
(2) 코디네이터의 의심스러운 상태를 경계하는 이유
- DB 는 트랜잭션이 커밋/어보트 완료될 때까지 잠금을 해제할 수 없다. 코디네이터 로그가 손실되면 잠금을 풀기 어렵거나 관리자가 수동으로 해결해야 한다.
- 또한 다른 트랜잭션이 자기의 일을 계속할 수 없고 블로킹된다.
(3) 분산 트랜잭션 제약
- 코디네이터가 단일 노드에서 실행되면 SPOF 가 된다.
- 코디네이터 로그를 지속적으로 관리해야 하므로 배포 특성이 바뀌게 된다. 배포로 인해 코디네이터 로그 손실이 발생하면 트랜잭션 복구가 어려워진다.
- 각 노드들 간의 호환돼야 하므로 최소 공통 분모가 될 필요가 있다.
- 이종 노드 간 교착 상태를 가지기 어렵다.
- 잠금에 대한 정보 교환을 위한 프로토콜 필요
- 직렬성 스냅숏 격리(SSI) 지원을 위한 프로토콜 필요 등등
- 시스템의 부분 결함이 장애를 증폭할 수 있는 위험이 있다.
7. 멤버십과 코디네이션 서비스 제공 기능
- 선형성 원자적 연산 : 원자적 compare-and-set 연산을 사용해 잠금을 구현할 수 있다. 합의 프로토콜은 노드에 장애가 나거나 어느 시점에 네트워크가 끊기더라도 그 연산이 원자성, 선형성을 보장한다.
- 연산의 전체 순서화 : 펜싱 토큰을 통해 각 연산에 전체 순서를 정하고 각 연산에 단조 증가 값(e.g. zxid, cversion) 을 할당해 제공한다.
- 장애 감지 : 클라이언트는 주키퍼 서버에 세션을 유지해 하트비트를 교환해 생존 여부를 체크한다.
- 변경 알림 : 다른 클라이언트가 생성한 잠금과 값을 읽을 수 있을 뿐만 아니라 거기에 변경이 있는지 감시할 수 있다.
'architecture' 카테고리의 다른 글
레이어드 아키텍처 (그런데, clean architecture를 곁들인) (0) | 2024.12.27 |
---|---|
[데이터 중심 애플리케이션 설계] 스트림 프로세싱 (0) | 2024.07.01 |
[데이터 중심 애플리케이션 설계] 분산 시스템의 골칫거리 (1) | 2024.06.08 |
[가상 면접 사례로 배우는 대규모 시스템 설계 기초 2] 호텔 예약 시스템 (0) | 2024.05.21 |
[가상 면접 사례로 배우는 대규모 시스템 설계 기초 2] 광고 클릭 이벤트 집계 (1) | 2024.05.11 |