복제란 네트워크로 연결된 여러 장비에 동일한 데이터의 복사본을 유지한다는 의미
데이터의 복제가 필요한 이유
- 지리적으로 사용자와 가깝게 데이터를 유지해 지연 시간을 줄인다.
- 일부 노드에 장애가 발생해도 동작할 수 있도록 해 가용성을 높인다.
- 읽기 질의를 제공하는 노드의 수를 확장해 읽기 처리량을 늘린다.
변경되는 데이터를 노드 간 복제하기 위한 대표적인 알고리즘
- 단일 리더
- 다중 리더
- 리더 없는복제 방식
1. 리더와 팔로워
DB의 복사본을 저장하는 각 노드 : 복제 서버(replica)
모든 복제 서버에 데이터가 복제된 것을 보장하기 위해 일반적으로 리더 기반 복제를 활용
- 복제 서버 중 하나를 리더(마스터, 프라이머리)로 지정 클라이언트로부터의 모든 쓰기 요청은 리더에게만 전달
- 리더는 자신의 서버에 데이터를 기록하고 팔로워(슬레이브, 세컨더리) 복제 서버에 쓰기 요청을 전달
- 클라이언트 관점에서 쓰기는 리더만 허용하므로 팔로워는 읽기 전용
- 리더가 팔로워에게 쓰기 내용을 전달할 땐 복제 로그(replication log)나 변경 스트림(change stream)을 주로 활용
- 리더기반 복제 모드는 여러 RDB에 내장되어 있고 분산 메시지 브로커에서도 사용
1-1. 동기식 대 비동기식 복제
- 동기식 복제의 장단점
- 장점: 팔로워가 리더와 일관성 있게 최신 데이터 복사본을 가지는 것을 보장
- 단점: 팔로워가 죽거나 네트워크 문제로 동기 팔로워가 응답하지 않을 경우 쓰기가 처리될 수 없음
- 임의 한 노드의 장애가 전체 시스템을 멈추게 하므로 모든 팔로워가 동기식인 상황은 비현실적
- 반동기식(semi-synchronouse)
- 현실적으로 팔로워 하나는 동기식으로, 나머지는 비동기식으로 복제하는 방식을 사용
- 적어도 두노드 (리더와 하나의 동기 팔로워)에 최신 복사본이 있는 것을 보장
- 비동기식 복제의 장단점
- 장점: 모든 팔로워가 잘못되더라도 리더가 쓰기 처리를 계속 할 수 있음
- 단점: 리더가 잘못되고 복구 불가능한 상황에서 팔로워에 아직 복제되지 않은 모든 쓰기는 유실 (지속성 보장 X)
- 비동기식 복제는 내구성을 약화시키지만 많은 팔로워가 있거나 지리적으로 분산되었다면 비동기식 복제를 사용
1-2. 새로운 팔로워 추가
- 팔로워가 추가되어 기존 리더의 데이터를 복제할 때 가용성을 위해 중단없이 수행가능해야 한다. 그 과정을 다음과 같다.
- 전체 DB를 잠그지 않고, 리더 DB의 스냅샷을 일정시점에 가져온다.
- 스냅숏을 새로운 팔로워 노드에 복사한다.
- 팔로워는 리더에 연결해 스냅샷 이후 발생한 모든 데이터 변경을 요청한다
- 팔로워가 스냅샷 이후 데이터 변경의 미처리분(backlog)을 모두 처리하면 따라잡았다고 말한다.
1-3. 노드 중단 처리
리더 기반 복제에서 고가용성 달성하는 방법
팔로워 장애 : 따라잡기 복구
각 팔로워는 리더로부터 수신한 데이터 변경 로그를 로컬 디스크에 보관, 팔로워 장애는 쉽게 복구 가능
- 보관된 로그에서 결함이 발생하기 전에 처리한 마지막 트랜잭션을 확인
- 팔로워는 리더에 연결해 끊어진 동안 발생한 데이터 변경을 모두 요청
리더 장애 : 장애 복구
- 장애 복구(failover)
- 팔로워 중 하나를 새로운 리더로 승격해야 하고, 클라이언트는 새로운 리더로 쓰기를 전송하기 위해 재설정이 필요
- 다른 팔로워는 새로운 리더로부터 데이터 변경을 소비하기 시작
- 참고. 장애 복구 단계
- 일부 운영팀은 소프트웨어가 자동 장애 복구를 지원하더라도 수동으로 장애 복구를 수행하는 방식이 선호
- 노드 장애, 불안정한 네트워크, 지속성, 가용성, 지연 시간 등의 문제는 분산 시스템에서 발생하는 근본적인 문제
1-4. 복제 로그 구현
구문 기반 복제
- 리더는 모든 쓰기 요청을 기록하고 쓰기를 실행한 다음 구문 로그를 팔로워에게 전송
- RDB는 INSERT, UPDATE, DELETE 구문을 팔로워에게 전달하고 각 팔로워는 SQL 구문을 파싱하고 실행
- 복제가 깨질 수 있는 다양한 사례
- 대안 해결책
- 리더가 구문을 기록할때 비결정적 함수의 고정 값을 반환하게끔 대처
- MySQL 5.1 이후 구문에 비결정성이 있는 경우 로우 기반 복제를 사용하도록 변경
쓰기 전 로그 배송
- 일반적으로 저장소 엔진은 모든 쓰기에 대한 로그 기록
- 리더는 디스크에 로그를 기록하는 일 외에도 팔로워에게 네트워크로 해당 로그를 전송
- 가장 큰 단점은 로그가 제일 저수준의 데이터를 기술
- 복제가 저장소 엔진과 밀접하게 결합되어 리더와 팔로워의 데이터베이스 소프트웨어 버전을 다르게 실행 할 수 없음
논리적(로우 기반) 로그 복제
- 복제 로그를 저장소 엔진 내부와 분리하기 위한 대안 하나는 복제와 저장소 엔진을 위해 다른 로그 형식을 사용하는 것
- 논리적 로그(lgical log) : 저장소 엔진의 (물리적) 데이터 표현과 구별하기 위한 복제 로그
- RDB용 논리적 로그는 대개 로우 단위로 DB 테이블에 쓰기를 기술한 레코드 열
- 여러 로우를 수정하는 트랜젝션은 여러 로그 레코드를 생성한 다음 트랜젝션이 커밋 되었음을 레코드에 표시
- 논리적 로그를 저장소 엔진 내부와 분리했으므로 하위 호환성을 더 쉽게 유지 가능
- 리더와 팔로워에서 다른 버전의 데이터베이스 소프트웨어나 저장소 엔진을 실행 가능
트리거 기반 복제
- 데이터의 서브셋만 복제하거나 데이터베이스를 다른 종류의 데이터베이스로 복제해야 하거나 충돌해소로직이 필요 한 경우
- 복제를 애플리케이션 층으로 옮김
- 트리거는 사용자 정의 애플리케이션 코드를 등록가능
- 데이터베이스 시스템에서 데이터 변경 시(쓰기 트랜젝션) 자동으로 실행
- 일반적으로 트리거 기반 복제는 다른 방식에 비해 오버헤드가 있지만 유연성 때문에 유용한 매우 유용
2. 복제 지연 문제
최종적 일관성(Eventual Consistency) : 데이터베이스에 쓰기를 멈추고 잠시동안 기다리면 팔로워는 결국 따라잡고 리더와 일치
2-1. 자신이 쓴 내용 읽기
- 자신이 쓴 내용을 바로 다시 읽기 했을 때 복제 서버엔 쓰기가 반영되지 않아 쓰기 전 데이터를 볼 가능성이 존재
- 쓰기 후 읽기 일관성(자신의 쓰기 읽기 일관성) 은 항상 자신이 갱신한 내용에 대해서는 일관성을 보장
- 단, 다른 사용자에 대해서는 일관성을 보장하지 않음
- (참고) 쓰기 후 읽기 일관성 구현방법
2-2. 단조 읽기
- 사용자가 시간이 거꾸로 흐르는 현상을 목격할 수 있음
- 복제 서버 A, B 중 A서버에만 동기화가 되어있는 시점에 사용자가 처음엔 A 서버를 통해 데이터를 읽을땐 데이터가 반환되지만 그 다음에 B 서버를 통해 데이터를 읽으면 데이터가 반환되지 않음
- 단조 읽기(monotonic read)는 이런 종류의 이상 현상이 발생하지 않음을 보장
- 각 사용자의 읽기가 항상 동일한 복제 서버에서 수행되게끔 함 (다른 사용자는 다른 복제 서버에서 읽을 수 있음)
2-3. 일관된 순서로 읽기
- 데이터가 서로 다른 파티션에 저장되고 파티션 리더가 다를 때 실제 시간상으론 A -> B 순으로 입력되었지만 팔로워들은 B -> A 순으로 복제가 될 가능성
- Observer는 실제로 쓰여진 순서가 아닌 반대로 읽는다
- 일관된 순서로 읽기(Consistent Prefix Read)는 이런 종류의 이상 현상이 발생하지 않음을 보장
- 특정 순서로 쓰기가 발생하면 이 쓰기를 읽는 모든 사용자는 같은 순서로 쓰여진 내용을 보게 됨을 보장
2-4. 복제 지연을 위한 해결책
- 최종적 일관성으로 인한 복제 지연이 애플리케이션에 얼마나 영향을 끼치는지 파악할 필요가 있다
- 지연에 대한 영향이 크다면 강력한 일관성을 보장할 수 있도록 시스템을 설계해야 한다
- 애플리케이션에서 강력한 보장을 제공할 수도 있지만 애플리케이션에서 다루기엔 복잡하다
- 트랜잭션은 이러한 문제에 대한 해결을 데이터베이스 단에서 보장해주지만
- 분산 데이터베이스로 전환되면서 많은 시스템이 트랜잭션을 포기했다
- 트랜잭션이 성능과 가용성 측면에서 너무 비싸고, 확장 가능한 시스템에서는 어쩔 수 없이 최종적 일관성을 사용해야한다는 주장이 존재.
2. 다중 리더 복제
다중리더 설정 : 쓰기를 허용하는 노드를 하나 이상 두는 것으로 자연스럽게 확장
2-1. 다중 리더 복제의 사용 사례
1) 다중 데이터센터 운영
- 각 데이터센터마다 리더가 존재하는 방식으로 다중 리더를 세팅
- 데이터센터간에는 각 데이터센터의 리더가 다른 데이터센터의 리더에게 변경사항을 복제
- 다중 리더 복제의 단점 : 동일한 데이터를 다른 두 개의 데이터 센터에서 변경할 경우 쓰기 충돌 해소 필요
2) 오프라인 작업을 하는 클라이언트
- 모든 디바이스에는 리더처럼 동작하는 로컬 데이터베이스가 존재 (쓰기 요청을 받아야 함)
- 디바이스의 인터넷이 연결되면 로컬에 변경된 데이터가 복제 서버로 동기화 된다.
- 아키텍처 관점에서 보면 이 설정은 근본적으로 데이터 센터 간 다중 리더 복제와 동일하다.
2-2. 쓰기 충돌 다루기
- 다중 리더 복제의 가장 큰 문제 : 쓰기 충돌
- 각 사용자의 변경을 로컬 리더에 적용하고 변경을 비동기로 복제시 출동을 감지
- 충돌 회피 : 충돌을 처리하는 제일 간단한 전략
- 일관된 상태 수렴 : 모든 복제 계획은 모든 복제 서버가 최종적으로는 동일하다라는 사실을 보장해야 한다
- 사용자 정의 충돌 해소 로직 : 전체 트랜젝션이 아닌 개별 로우나 문서 수준에서 적용된다
2-3. 다중 리더 복제 토폴로지
- 복제 토폴로지는 쓰기를 한 노드에서 다른 노드로 전달하는 통신 경로를 말한다.
- 리더가 둘 이상이라면 다양한 토폴로지가 가능하다.
- 전체 연결 토폴로지는 모든 리더가 각자의 쓰기를 다른 모든 리더에서 전송한다
- 원형 토폴로지는 각 노드가 하나의 노드로부터 쓰기를 받고, 이 쓰기를 다른 한 노드에 전달한다.
- 별 모양 토폴로지는 지정된 루트 노드 하나가 다른 모든 노드에 쓰기를 전달한다. 트리로 일반화된다.
3. 리더가 없는 복제
일부 데이터 저장소 시스템은 리더의 개념을 버리고 모든 복제 서버가 클라이언트로부터 쓰기를 직접 받을 수 있게 허용하는 접근 방식을 사용하기도 한다.
리더 없는 복제는 아마존이 내부 다이나모 시스템에서 사용한 후 다시 데이터베이스 아키텍처로 유행했다.
리악, 카산드라, 볼드모트는 다이나모에서 영감을 얻은 리더 없는 복제 모델의 오픈소스 데이터스토어이며 이런 종류의 데이터베이스를 다이나모 스타일이라고 부른다.
3-1. 노드가 다운됐을 때 데이터 베이스에 쓰기
- 읽기 복구와 안티 엔트로피
- 복제 계획은 최종적으로 모든 데이터가 모든 복제 서버에 복사됨을 보장해야 한다.
- 다이나모 스타일은 누락된 쓰기 처리를 위해 두 가지 메커니즘을 주로 사용한다.
- 읽기 복구
- 안티 엔트로피 처리
- 읽기 복구만 사용하는 경우 값을 읽을 때만 복구가 가능하므로 읽지 않는 값은 일부 복제본에 누락될 가능성이 존재한다.
- 읽기와 쓰기를 위한 정족수
- n개의 복제 서버가 있을 때 w개의 노드에 쓰기 성공이 보장되고 읽기는 r개 노드로 수행되는 경우에 w + r > n의 경우 항상 최신 값을 읽는 것을 기대한다.
- 정족수(Quorum) 읽기와 쓰기인 경우 위 식을 보장한다.
3-2. 정족수 일관성의 한계
- 모든 읽기가 항상 최신값을 반환함을 보장하는 경우 흔히 Strong Consistency(강력한 일광성)을 보장한다고 한다.
- 보통 w + r > n인 경우 강력한 일관성이 보장된다고 하지만 정족수 읽기와 쓰기인 경우 강력한 일관성을 보장하지 않는 에지 케이스가 존재한다.
- 최신성 모니터링
- 리더 기반 복제에서 데이터베이스는 일반적으로 복제 지연에 대한 지표를 노출한다
- 리더없는 복제 서비스에서 복제 서버의 오래됨을 측정하고 매개변수에 따라 오래된 값을 읽는 비율을 예측하는 연구가 있었다
- 최종적 일관성은 의도적으로 모호한 보장이지만 운용성을 위해서는 최종적 을 정량화 할 수 있어야 한다
3-3. 느슨한 정족수와 암시된 핸드오프
- 적절히 설정된 정족수가 있는 데이터베이스는 장애 복구 없이 개별 노드 장애를 용인한다
- 노드가 n개 이상인 대규모 클러스터에서 특정 값을 위한 정족수가 충족되지 못하더라도 n에 포함되지 않은 다른 노드에 우선적으로 쓰기를 기록하는 방식을 느슨한 정족수라고 부른다
- 느슨한 정족수는 쓰기 가용성을 높이는데 유용하지만 w + r > n인 경우에도 강력한 일관성을 보장하지 않는다
- 느스한 정족수는 모든 일반적인 다이나모 수현에서 선택 사항이다
3-4. 동시 쓰기 감지
- 다이나모 스타일 데이터베이스는 여러 클라이언트가 동시에 같은 키에 쓰는 것을 허용하므로 엄격한 정족수를 사용하더라도 충돌이 발생한다.
- 동시 쓰기가 발생했을 때 네트워크 지연이나 부분적인 장애로 인해 쓰기 요청이 다른 노드에 다른 순서로 도착하여 일관성이 깨질 수 있다.
- 최종적 일관성을 달성하기 위해 복제본들은 동일한 값이 돼야 한다
- 쓰기 충돌 해소를 위한 기법
- 최종 쓰기 승리(동시 쓰기 버리기)
- 예전 값은 버리고 최신 값으로 덮어쓰는 방법이다.
- 어떤 쓰기가 최신인지 명확하게 결정할 수 있는 한 최종적 일관성이 보장된다.
- 카산드라에서 유일하게 제공하는 충돌 해소 방법이고 리악에서는 선택적 기능이다.
- LWW로 데이터베이스를 안전하게 사용하는 유일한 방법은 키를 한번만 쓰고 이후에는 불변 값으로 다루는 것이다.
- 이전 발생 관계와 동시성
- 사실 작업이 다른 작업보다 먼저 발생하지 않으면 단순히 동시 작업이라 말한다
- 동시에 쓴 값 병합
- 형제 : 여러 작업이 동시에 발생한 경우 클라이언트가 합쳐 정리한 동시에 쓴 값
- 서버는 모든 키에 대해 버전을 기록하고 클라이언트가 키를 읽을 때 최신 버전뿐만 아니라 덮어쓰지 않은 모든 값을 병합하여 반환한다
- 톰스톤 : 버전 값들을 병합할 때 상품이 제거되었음을 알 수 있게 남겨 둔 삭제 표시
- 최종 쓰기 승리(동시 쓰기 버리기)
'Books' 카테고리의 다른 글
| [대규모 중심 어플케이션 설계] 07장. 트랜잭션 (1) | 2025.01.23 |
|---|