Search

5장 - 복제

Date
2025/01/19 06:07
Tags

1. 분산 데이터

1.1. 여러 장비간 분산된 데이터베이스를 필요로 하는 이유

확장성
데이터 볼륨, 읽기 부하, 쓰기 부하가 단일 장비에서 다룰 수 있는 양보다 커지면 부하를 여러 장비로 분배 가능
내결함성/고가용성
장비 하나가 죽더라도 애플리케이션이 계속 동작해야 한다면 여러 장비를 사용해 중복성 제공 가능
지연시간
사용자와 지리적으로 가까운 데이터센터에서 서비스를 제공하기 위해 전 세계 다양한 곳에 서버를 두어 지연을 낮춘다

1.2. 고부하로 확장

1.2.1. 공유 아키텍처

수직 확장을 통해 공유 메모리 아키텍처로 단일 장비처럼 다룰 수 있다.
하지만 많은 비용이 들고, 병목 때문에 두 배의 용량을 높힌다고 반드시 두 배의 부하를 처리할 수 있는 것이 아니다.

1.2.2. 비공유 아키텍처

수평 확장을 통해 독립적인 컴퓨팅 자원을 가지는 노드를 생성한다.
가성비가 좋은 시스템을 사용할 수 있고 지리적 영역에 걸쳐 데이터를 분산해 지연시간을 줄일 수 있다.

1.3. 복제 대 파티셔닝

복제 = 같은 데이터를 다른 위치에 있는 여러 노드에 유지한다.
파티셔닝 = 큰 데이터베이스를 파티션이라는 작은 서브셋으로 나누고 각 파티션은 각기 다른 노드에 할당한다.

2. 복제

복제란 네트워크로 연결된 여러 장비에 동일한 데이터의 복사본을 유지한다는 의미다.
복제가 필요한 이유
지리적으로 사용자와 가깝게 데이터를 유지해 지연 시간을 줄인다.
시스템의 일부에 장애가 발생해도 지속적으로 동작할 수 있게 해 가용성을 높인다.
읽기 질의를 제공하는 장비의 수를 확장해 읽기 처리량을 늘린다.
복제에서 모든 어려움은 변경 처리에 있으며 노드가 변경을 복제하기 위한 알고리즘으로 단일 리더, 다중 리더, 리더 리스 복제를 살펴본다.
복제는 고려해야 할 트레이드오프가 있다.

2.1. 리더와 팔로워

데이터베이스의 복사본을 저장하는 각 노드를 복제 서버(replica)라고 한다.
데이터베이스의 모든 쓰기는 모든 복제 서버에 처리돼야 한다.
가장 일반적인 해결책은 리더 기반 복제다.
리더-팔로워 모델
복제 서버 중 하나를 리더로 정하고 리더는 로컬 저장소에 새로운 데이터를 저장한다.
다른 복제 서버는 팔로워가 되어 변경을 복제 로그나 변경 스트림을 통해 전송받아 복사본을 갱신한다.

2.2. 동기식 대 비동기식 복제

동기식과 비동기식의 차이
팔로워1은 동기식, 팔로워2는 비동기식 복제를 사용하고 있다.
동기식 복제의 장단점
리더와 일관성 있게 최신 데이터 복사본을 가지는 것을 보장
동기 팔로워가 응답하지 않는다면 쓰기가 처리될 수 없다.
리더는 모든 쓰기를 block하고 동기 복제 서버가 다시 사용할 수 있을 때까지 기다려야 한다.
때문에 모든 시스템이 동기식이라면 전체 시스템이 멈추게 된다.
동기식, 비동기식을 섞어 사용하는걸 반동기식이라 한다.
보통 리더 기반 복제는 완전히 비동기식으로 구성한다.
비동기식 복제는 내구성을 약화시키기 때문에 나쁜 트레이드오프 같지만 많은 팔로워가 있거나 지리적으로 분산됐다면 비동기식 복제를 널리 사용한다.

2.3. 새로운 팔로워 설정

때때로 복제 서버 수를 늘리거나 장애 노드의 대체를 위한 새로운 팔로워를 설정해야 한다.
클라이언트는 지속적으로 DB에 기록하고 데이터는 항상 유동적이기 때문에 표준 파일 복사본은 다른 시점에 데이터베이스의 다른 부분을 보게 된다.
즉 복사 결과가 유효하지 않을 수 있다.
DB를 잠가 디스크의 일관성을 만들 수 있지만 고가용성 목표에 부합하지 않기 때문에 아래 과정을 통해 진행한다.
DB잠금을 걸지 않고 리더의 스냅숏을 일정 시점에 가져온다. (MySQL의 innobackupex 툴)
스냅숏을 새로운 팔로워에 복사한다.
팔로워는 리더에 연결해 스냅숏 이후 데이터 변경을 요청한다. (로그 일련번호와 이진로그 좌표를 통해 진행)
팔로워가 스냅숏 이후 데이터 변경의 미처리분을 모두 처리했을 때 따라잡았다고 말한다.

2.4. 노드 중단 처리

노드가 장애로 인해 예기치 않게 중단될 경우 리더 기반 복제에서 고가용성을 달성할 수 있는 방법

2.4.1. 팔로워 장애: 따라잡기 복구

결함이 발생하기 전 마지막 트랜잭션을 알아내 끊어진 이후 발생한 데이터 변경을 모두 요청한다.

2.4.2. 리더 장애: 장애 복구

리더의 장애를 처리하는 일은 까다롭다 팔로워 중 하나를 리더로 승격하고 클라이언트는 새로운 리더로 쓰기를 전송해야하는 재설정이 필요하다.
장애 복구 단계
리더가 장애인지 판단한다.
새로운 리더를 선택한다.
새로운 리더 사용을 위해 시스템을 재설정 한다.

2.5. 복제 로그 구현

2.5.1. 구분 기반 복제

리더는 모든 쓰기 요청을 기록하고 쓰기를 실행한 다음 구문 로그를 팔로워에게 전송한다.
합리적인 것 같은 이 접근법도 복제가 깨질 수 있는 사례가 존재한다.
NOW(), RAND() 등은 복제 서버마다 다른 값을 생성할 가능성이 존재
자동증가 컬럼을 사용하거나, 기존 데이터에 의존하는 경우(WHERE) 정확히 같은 순서로 실행되어야 함
순서가 다르면 구문의 효과가 다를 수 있음 → 동시에 여러 트랜잭션이 수행되는 것을 제한한다.
부수효과를 가진 구문의 경우 부수효과가 완벽히 결정적이어야 모든 팔로워에서 그 효과도 동일하다.
트리거, 스토어드 프로시저, 사용자 정의 함수
이 문제의 해결책으로 리더는 구문을 기록할 때 모든 비결정적 함수 호출을 고정 값을 반환하게끔 대체할 수 있다.
But. 여러 에지 케이스가 있기에 일반적으로 다른 복제 방법을 선호한다.

2.5.2. 쓰기 전 로그 배송

일반적으로 데이터베이스의 모든 쓰기는 로그에 기록이 된다.
리더가 로그를 팔로워에게 전송하고, 팔로워는 이 로그를 처리함으로써 복제한다.
로그는 제일 저수준의 데이터를 기술함
디스크 블록에서 어떤 데이터를 변경했는 지와 같은 상세 정보 포함
복제 프로세스가 저장소 엔진과 밀접하게 연관된다.
리더와 팔로워가 동일한 소프트웨어 버전에서 실행되어야 한다.
소프트웨어 업그레이드 시 중단 시간이 필요하다.

2.5.3. 논리적(로우 기반) 로그 복제

로그를 저장소 엔진과 분리하기 위한 대안으로 복제와 저장소 엔진에 각기 다른 로그 형식을 사용한다.
이와 같이 복제에서 사용하는 로그를 저장소 엔진의 물리적 데이터 표현과 구별하기 위해 논리적 로그라 부른다.
RDB의 논리적 로그는 대개 로우 단위이고, 테이블에 쓰기를 기술한 레코드 열이다.
삽입된 로우 = 모든 컬럼의 새로운 값을 포함
삭제된 로우 = 로우를 식별하기 위한 정보(보통 기본 키)를 포함
갱신된 로우 = 로우를 식별하기 위한 정보 + 모든 컬럼의 새로운 값 또는 변경된 컬럼의 새로운 값
여러 로우를 수정하는 트랜잭션은 여러 로그 레코드를 생성한 다음 트랜잭션 커밋을 레코드에 표시한다.
논리적 로그와 저장소 엔진 내부를 분리해 하위 호환성을 쉽게 유지하고, 리더와 팔로워간 다른 버전의 소프트웨어 실행이 가능
논리적 로그는 외부 애플리케이션이 파싱하기 쉽다.
데이터 웨어하우스와 같은 외부 시스템에 데이터베이스 내용 전송 시 유용하다.
변경 데이터 캡쳐(CDC, change data capture)

2.5.4. 트리거 기반 복제

지금까지 설명한 복제 방식은 애플리케이션의 관여 없이 DB 시스템에 의해 구현되었다.
오라클은 GoldenGate 도구를 통해 데이터베이스 로그를 읽어 애플리케이션이 데이터를 변경할 수 있도록 한다.
사용자 정의 애플리케이션 코드를 등록할 수 있다.
데이터 변경 시(쓰기 트랜잭션) 자동으로 실행된다.
트리거를 통해 데이터 변경을 분리된 테이블에 로깅한다.
이 테이블에 기록된 데이터 변경을 외부 프로세스가 읽고 처리한다.
필요한 애플리케이션 로직 적용 후 다른 시스템에 데이터 변경을 복제한다.
트리거의 장점
유연성 높아 매우 유용하다.
트리거의 단점
트리거 기반 복제는 다른 복제 방식보다 많은 오버헤드가 있다.
그리고 데이터베이스에 내장된 복제보다 버그나 제한 사항이 더 많이 발생한다.

2.6. 복제 지연 문제

복제는 노드 내결함성 뿐만 아닌 확장성과 지연 시간 때문에도 필요하다.
읽기 확장 아키텍쳐
리더 기반 복제는 쓰기 분산은 어렵지만 읽기 분산이 가능하다.
장점
웹 서비스의 경우 읽기 요청이 대부분, 쓰기 요청은 작은 비율로 구성
리더의 부하를 경감하고 복제 서버에서 읽기 요청을 처리 → 웹 서비스에 적합
복제의 목표 중 확장성, 지연시간 단축 달성
단점
비동기식 복제 방식에서만 동작한다.
동기식 복제는 단일 노드의 장애, 네트워크 중단이 전체 시스템의 쓰기를 마비시켜 적절하지 않음
노드가 많아질 수록 다운될 가능성이 커져 동기식 설정과 적절하지 않다.
데이터 불일치 = 팔로워와 리더간 일시적 복사 지연 상태
최종적 일관성 = 팔로워는 리더와 데이터의 불일치 상태가 있을 수 있지만 쓰기를 멈추면 결국 리더를 따라잡게 된다.
복제 지연으로 인한 데이터 불일치는 찰나의 순간이고 크게 문제가 되지 않지만 시스템이 가용량 근처에서 동작하거나 네트워크 문제가 있다면 복제 지연으로 인한 불일치가 문제가 된다.

2.6.1. 복제지연 사례

자신이 쓴 내용 읽기
단조 읽기
일관된 순서로 읽기

2.6.2. 복제 지연 해결책

해결 방안의 종류
쓰기 후 읽기와 같은 강한 보장을 제공하도록 시스템을 설계한다.
비동기식 복제를 사용하지만 동기식 방식으로 동작하는 것 처럼 보인다.
애플리케이션이 데이터베이스보다 더 강력한 보장을 제공하는 방법도 있다.
그러나 애플리케이션에서 다루기에는 복잡하여 잘못되기 쉽다.
트랜잭션 = 애플리케이션이 단순해지기 위해 데이터베이스가 더 강력한 보장을 제공하는 방법
애플리케이션 개발자가 미묘한 복제 문제를 걱정하지 않고 올바른 작업 수행을 위해 항상 데이터베이스를 신뢰할 수 있다.
이것이 트랜잭션이 존재하는 이유다.
분산 시스템에서의 트랜잭션은 많은 시세틈에 포기했다.
트랜잭션은 가용성과 성능 측면에서 너무 비싸다.
확장 가능한 시스템에서는 최종적 일관성을 선택하는 것이 불가피하다는 주장도 존재한다.
이러한 주장의 일부는 사실이지만 지나치게 단순화된 측면이 있다.

3. 다중 리더 복제

리더 기반 복제는 하나의 리더를 거쳐야만 쓰기가 가능하다는 것이다.
쓰기 처리를 하는 각 노드는 데이터 변경을 모든 노드에 전달하는데 이를 다중 리더 설정이라 부른다.
이 설정에서 각 리더는 동시에 다른 리더의 팔로워 역할도함
모든 쓰기를 해당 리더를 거쳐야 하고, 리더 연결이 불가능한 경우 쓰기 불가능한 단점 보완

3.1 다중 리더 복제의 사용 사례

3.1.1 다중 데이터 센터 운영

각 데이터 센터마다 리더가 있을 수 있다.
각 데이터 센터내 리더 팔로워 복제를 사용
각 데이터 센터 간 리더가 다른 데이터 센터 리더에게 변경 사항 복제한다.
동일한 데이터를 다른 두 개의 데이터센터에서 동시에 변경 가능하므로 쓰기 충돌은 반드시 해소되어야함
일부 데이터베이스는 기본적으로 다중 리더 설정 제공
단일 리더와 다중 리더간의 설정 비교
#
단일 리더
다중 리더
성능
쓰기는 인터넷을 통해 리더가 있는 데이터 센터로 이동해야해서 쓰기 지연 발생
쓰기는 로컬 데이터센터 처리 후 비동기 방식으로 다른 데이터센터에 복제
데이터센터 중단 내성
리더가 있는 데이터센터가 고장 나면 다른 데이터센터의 팔로워를 리더로 승진
각 데이터센터 독립적으로 동작, 고장난 데이터센터가 온라인으로 돌아왔을때 복제
네트워크 문제 내성
데이터 센터 내 쓰기는 동기식이기에 데이터 센터 내 연결 문제에 민감
비동기 복제를 사용해 네트워크 문제에 보다 잘 견딤, 일시적 네트워크 중단에도 쓰기 처리는 진행되기 때문

3.1.2. 오프라인 작업을 하는 클라이언트

인터넷 연결이 끊어진 동안 애플리케이션 동작해야 하는 경우
협업 편집 : 동시에 여러 사람이 문서를 편집할 수 있는 애플리케이션 ex) 이더패드, 구글 독스 등

3.2. 쓰기 충돌 다루기

다중 리더 복제에서 가장 큰 문제는 쓰기 충돌이 발생한다는 점이다.
각 사용자가 동시에 편집 후 로컬 리더에 저장하였으나 변경을 비동기로 복제 시 쓰기 충돌 발생

3.2.1. 동기 대 비동기 충돌 감지

동기식으로 충돌 감지를 하면 다중 리더 복제의 장점을 잃어버림

3.2.2. 충돌 회피

충돌 처리하는 가장 간단한 전략
충돌 처리가 어려워 충돌 피하는 것이 자주 권장됨
특정 레코드의 모든 쓰기를 동일한 리더에서 처리함

3.2.3. 일관된 상태 수렴

모든 복제 서버가 동일해야 하는 원칙으로 일괄성없는 상태를 용인하지 않는다.
수렴(convergent) : 모든 변경이 복제돼 모든 복제 서버에 동일한 최종 값이 전달되게 해야 함

3.2.4. 사용자 정의 충돌 해소 로직

충돌 해소의 가장 적합한 방법은 애플리케이션에 따라 다르다.
따라서 대부분 다중 리더 복제도구는 애플리케이션 코드를 사용해 충돌 해소 로직 작성
쓰기 수행 중
- 복제된 변경사항 로그에서 데이터베이스 시스템 충돌 감지되면 충돌 핸들러 호출 - 백그라운드에서 실행됨
읽기 수행 중
- 충돌 감지 시 모든 충돌 쓰기 저장 - 다음 번 읽기 시 여러 데이터 반환. 애플리케이션은 사용자에게 충돌 내용 보여주거나 자동으로 충돌 해소해 결과를 데이터베이스에 기록 - 카우치DB가 이렇게 동작
자동 충돌 해소
충돌 없는 복제 데이터 타입
Set, Map, 정렬 목록, 카운터 등을 위한 데이터 구조의 집합
병합 가능한 영속 데이터 구조
Git 처럼 명시적으로 히스토리 추적하고 삼중 병합 함수를 사용한다
운영 변환
이더패트, 구글 독스 같은 협업 편집 애플리케이션의 충돌 해소 알고리즘

3.3. 다중 리더 복제 토폴로지

복제 토폴로지는 쓰기를 한 노드에서 다른 노드로 전달하는 통신 경로를 설명
리더가 둘 이상이라면 다양한 토폴로지가 가능하다.
원형 토폴로지
각 노드가 하나의 노드로부터 쓰기를 받고, 이 쓰기를 다른 노드에 전달
MySQL 에서 기본적으로 제공
노드 장애 시 노드 간 복제 메시지 흐름에 방해를 줌
별 모양 토폴로지
지정된 루트 노드 하나가 다른 모든 노드에 쓰기 전달
트리로 일반화 가능
노드 장애 시 노드 간 복제 메시지 흐름에 방해를 줌
다중 리더 복제의 쓰기가 잘못된 순서로 도착할 수 있다.
전체 연결 토폴로지
모든 리더가 각자의 쓰기를 다른 모든 리더에 전송
가장 일반적인 토폴로지
내결함성이 상대적으로 좋음
전체 연결 토폴로지의 문제점
네트워크 연결 속도 차이로 인한 복제 메시지 추월
리더2는 삽입 이전에 갱신을 처리하게 됨
올바른 이벤트 정렬을 위한 버전 벡터 기법으로 해결 가능
따라서 다중 리더 복제 시스템을 사용하려면 이런 문제를 인지하고 문서를 주의깊게 읽은 다음 데이터 베이스를 철저하게 테스트해봐야 함

4. 리더 없는 복제

리더의 개념을 버리고 복제 서버가 클라이언트로부터 쓰기를 직접 받을 수 있게 허용하는 접근 방식
다이나모 스타일 DB로 리악, 카산드라, 볼드모트 등 오픈소스 데이터스토어가 있음
일부 리더 없는 복제 구현에서는 클라이언트가 여러 복제 서버에 쓰기를 직접 전송하는 반면 코디네이터 노드가 클라이언트를 대신해 이를 수행하기도 한다.

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

정족수(quorum) 쓰기, 정족 수 읽기와 노드 중단 후 읽기 복구
다운된 노드에서는 쓰기가 누락되어 오래된(outdated) 값을 읽게 됨
읽기 요청을 병렬로 여러 노드에 전송해 최신 값을 읽어와 해결 가능
버전 숫자를 통해 읽어온 값 중 최신 값을 결정함

4.1.1. 읽기 복구와 안티 엔트로피

복제 계획은 최종적으로 모든 데이터가 모든 복제 서버에 복사된 것을 보장해야 한다.
다이나모 스타일 데이터스토어는 두 가지 메커니즘을 주로 사용
읽기 복구
- 클라이언트가 여러 노드에서 병렬로 읽기 수행하면 오래된 응답 감지 가능 - 복제 서버의 오래된 값을 새로운 값으로 기록 - 값을 자주 읽는 상황에 적합
안티 엔트로피 처리
- 백그라운드 프로세스와 복제 서버 간 데이터 차이를 찾아 누락된 데이터를 복사 - 특정 순서로 쓰기를 복사하기 때문에 지연이 있을 수 있음

4.1.2. 읽기와 쓰기를 위한 정족수

모든 성공한 쓰기가 세 개의 복제 서버 중 적어도 두 개의 복제 서버에 존재한다는 것을 보장한다면 하나의 복제 서버가 오래된 데이터임을 의미
두 개의 복제 서버에서 읽으면 두 개 중 적어도 하나는 최신 값인지 확인할 수 있다.
n개의 복제 서버가 있을 때 w개의 노드에서 성공해야 쓰기가 확정되고 모든 읽기는 최소한 r개의 노드에 질의해야 한다.
이런 r과 w를 따르는 읽기와 쓰기를 정족수 읽기와 쓰기라 부른다.
다이나모 스타일 DB에서는 복제 서버(n), 쓰기 노드(w), 읽기 노드(r) 설정 가능하다.
일반적으로 n 은 3 또는 5 등의 홀수, w = r = (n+1) / 2 (반올림) 설정한다.