복제
같은 데이터를 여러 곳에 사본으로 두는 일입니다. 한 곳이 멈춰도 남은 곳이 그대로 답합니다. 대신 사본은 원본이 바뀔 때마다 다시 맞춰야 합니다. 그 맞추는 일이 복제의 절반입니다.
상세
학급 알림장을 반장 혼자 들고 있으면 반장이 결석한 날 아무도 못 봅니다. 그래서 같은 알림장을 여러 명이 나눠 갖습니다. 새 내용을 옮겨 적기 전까지는 누구의 알림장을 보느냐에 따라 답이 달라집니다.
복제는 같은 데이터를 여러 대의 기계에 사본으로 두고, 한쪽에서 생긴 변경을 나머지 사본에 계속 전파해 같은 상태로 맞춰 나가는 일입니다. 사본 하나하나를 복제본이라고 부릅니다. 사본을 한 번 떠 두는 것만으로는 복제가 아닙니다. 변경이 계속 흘러가는 것까지가 복제입니다.
이것을 형식으로 적은 것이 복제 상태 기계입니다. 합의 알고리즘 Raft 의 원 논문은 이 방식을 여러 서버 위의 상태 기계가 같은 상태의 동일한 사본을 계산하고, 서버 일부가 내려가도 계속 동작하는 구조라고 적습니다. 구현은 대개 복제 로그로 합니다. 서버마다 명령이 줄지어 든 로그를 두고, 상태 기계가 그 명령을 순서대로 실행합니다. 모든 로그가 같은 명령을 같은 순서로 담고 상태 기계가 결정적이면, 서버마다 같은 상태와 같은 출력 순서가 나옵니다. 그 로그를 어긋나지 않게 지키는 것이 합의 알고리즘의 일입니다.
사본을 언제 맞췄다고 칠지가 계약의 나머지 절반입니다. 동기 복제는 사본이 반영을 마쳤다고 답할 때까지 쓰기를 완료로 치지 않습니다. 비동기 복제는 원본이 먼저 완료로 답하고 전파는 뒤따릅니다. 무엇을 기다리느냐만 다르고, 사본을 맞춘다는 계약 자체는 같습니다.
배경
기계는 멈춥니다. 데이터가 한 대에만 있으면 그 한 대가 멈추는 순간 읽지도 쓰지도 못합니다. 디스크가 깨지면 데이터 자체가 사라집니다. Raft 원 논문은 합의 알고리즘이 여러 대를 하나의 일관된 집단으로 묶어 그중 일부의 고장을 견디게 한다고 적습니다. 그래서 신뢰할 수 있는 대규모 소프트웨어 시스템을 짓는 데 핵심 역할을 한다고 적습니다.
고장만 문제인 것도 아닙니다. 읽기가 한 대에 전부 몰리면 그 한 대가 처리량의 상한이 됩니다. 사용자가 지구 반대편에 있으면 요청이 그 거리를 왕복해야 합니다. 셋 다 답이 같습니다. 데이터를 한 벌만 두지 않는 것입니다.
그래서 같은 데이터를 여러 곳에 두고 사본들이 계속 같아지게 만드는 장치를 씁니다. 한 대가 멈춰도 남은 사본이 답하고, 읽기를 여러 사본에 나눠 받고, 사용자와 가까운 곳에 사본을 둘 수 있습니다. 이 장치를 복제라고 부릅니다.
갈래
축은 하나입니다. 쓰기를 어디서 받느냐입니다.
단일 리더
쓰기를 받는 사본을 하나로 못 박습니다. PostgreSQL 문서는 데이터를 고칠 수 있는 서버를 read/write · master · primary 라 부르고, primary 의 변경을 따라가는 서버를 standby 또는 secondary 라 부릅니다. 승격되기 전에는 접속조차 받지 못하는 standby 를 warm standby, 접속을 받아 읽기 전용 질의를 처리하는 standby 를 hot standby 라 부릅니다.
Kafka 는 같은 구조를 파티션 단위로 씁니다. 복제 단위는 토픽 파티션이고, 고장이 없는 동안 파티션마다 리더 하나와 팔로워 0개 이상이 있습니다. 쓰기는 전부 리더로 가고 읽기는 리더나 팔로워로 갑니다. 팔로워는 보통의 컨슈머처럼 리더에게서 메시지를 받아 자기 로그에 적습니다.
sequenceDiagram
participant 앱
participant 리더
participant 팔로워
Note over 앱,팔로워: 동기 방식 — 팔로워 반영을 기다렸다가 완료를 보낸다
앱->>리더: 쓰기
팔로워->>리더: 변경을 받아 간다
Note over 팔로워: 자기 로그에 적는다
리더-->>앱: 완료
무엇을 기다리느냐가 이 방식의 대가를 정합니다. PostgreSQL 문서는 동기 방식이면 모든 서버가 트랜잭션을 커밋하기 전까지 커밋으로 치지 않으므로, 페일오버가 데이터를 잃지 않고 부하 분산된 서버가 어느 쪽에 물어도 일관된 결과를 준다고 적습니다. 비동기 방식이면 커밋과 전파 사이에 지연이 생겨, 백업 서버로 넘어갈 때 일부 트랜잭션이 사라질 수 있고 부하 분산된 서버가 조금 낡은 결과를 돌려줄 수 있다고 적습니다. 동기 방식의 지연을 감당할 수 없을 때 비동기 통신을 쓴다고 덧붙입니다.
다중 리더
쓰기를 받는 자리가 둘 이상입니다. CouchDB 문서는 복제를 원본 데이터베이스와 대상 데이터베이스 사이의 작업으로 적습니다. 작업이 끝난 시점에 원본의 활성 문서가 전부 대상에도 있고, 원본에서 지워진 문서는 대상에서도 지워져 있는 것이 목표입니다.
복제 작업 하나는 한 방향으로만 변경을 옮깁니다. 그래서 양쪽이 서로의 리더가 되는 master-master 복제는 방향이 반대인 복제 작업 두 개를 걸어 만듭니다. A 에서 B 로 옮겨진 변경을 B 에서 A 로 가는 작업이 다시 만나면, 그 변경이 이미 A 에 있다는 것을 알아채고 다음 변경을 기다립니다.
sequenceDiagram
participant A as 사본 A
participant B as 사본 B
A->>B: 복제 작업 1 · A 의 변경
B->>A: 복제 작업 2 · B 의 변경
Note over A: 이미 있는 변경이면 넘어간다
리더 없는 복제
쓰기를 받는 자리를 못 박지 않습니다. 몇 개의 사본이 답하면 됐다고 칠지로 정합니다. Cassandra 문서는 이것을 조정 가능한 일관성이라 부릅니다. 읽기에 참여하는 노드 수 R 과 쓰기에 참여하는 노드 수 W 의 합을 복제 계수 N 보다 크게 잡는 Dynamo 의 방식을 자기 판으로 가진 것이라고 적습니다. 운영자가 R 과 W 를 직접 고르는 대신 일관성 수준 중에서 고릅니다. ONE 은 사본 하나만 답하면 되고, QUORUM 은 과반인 n/2 + 1 이 답해야 하고, ALL 은 전부 답해야 합니다. LOCAL_QUORUM 은 코디네이터가 있는 데이터센터 안의 과반입니다.
쓰기 자체는 일관성 수준과 무관하게 언제나 모든 사본으로 보냅니다. 수준이 정하는 것은 코디네이터가 클라이언트에게 답하기 전에 몇 개의 응답을 기다리느냐뿐입니다.
sequenceDiagram
participant 앱
participant 코디네이터
participant 사본
앱->>코디네이터: 쓰기
코디네이터->>사본: 모든 사본으로 보낸다
사본-->>코디네이터: 응답
Note over 코디네이터: 일관성 수준이 정한 개수까지만 기다린다
코디네이터-->>앱: 완료
예시
PostgreSQL 스트리밍 복제
wal_level = replica
max_wal_senders = 10
primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass options=''-c wal_sender_timeout=5000'''
wal_level 은 WAL(Write-Ahead Log, 미리 쓰기 로그)에 얼마나 많은 정보를 적을지 정합니다.
기본값 replica 는 WAL 아카이빙과 복제를 지원할 만큼을 적고, standby 에서 읽기 전용 질의를
돌리는 것까지 포함합니다. minimal 은 크래시 복구에 필요한 것만 남기고, logical 은 논리적
디코딩에 필요한 정보를 더합니다. minimal 은 시점 복구에 필요한 정보를 담지 않습니다.
이 수준에서는 max_wal_senders 가 0 이 아니면 서버가 아예 시작하지 않습니다.
max_wal_senders 는 standby 서버나 스트리밍 베이스 백업 클라이언트에서 오는 동시 접속의
최대 개수입니다. 기본값은 10 이고 0 은 복제를 끕니다. standby 접속을 받으려면 wal_level 이
replica 이상이어야 합니다. primary_conninfo 는 standby 가 어느 primary 에 붙을지 적는
줄입니다. standby 는 몇 대든 둘 수 있고, 스트리밍 복제를 쓴다면 그것들이 동시에 붙을 수 있도록
primary 의 max_wal_senders 를 충분히 크게 잡아야 합니다. 사본의 첫 벌은 pg_basebackup
으로 뜹니다. 돌고 있는 클러스터의 베이스 백업을 다른 클라이언트에 영향을 주지 않고 뜨고,
시점 복구에도 쓰고 로그 시핑이나 스트리밍 복제 standby 의 출발점으로도 씁니다.
Redis
replicaof 192.168.1.1 6379
복제본 설정 파일에 이 한 줄을 넣으면 복제가 걸립니다. REPLICAOF 명령을 불러도 됩니다.
그러면 마스터 호스트가 복제본과 동기화를 시작합니다. Redis 는 기본으로 비동기 복제를 씁니다.
마스터는 명령이 복제본에서 처리되기를 매번 기다리지 않습니다. 대신 복제본이 받은 데이터 양을
주기적으로 비동기 확인응답으로 알려주므로, 마스터는 필요할 때 어느 복제본이 어디까지 처리했는지
압니다. 클라이언트는 WAIT 명령으로 특정 데이터에 대해 동기 복제를 요청할 수 있습니다. 다만
WAIT 은 지정한 개수만큼의 사본이 확인응답을 했다는 것만 보장합니다. Redis 인스턴스 집합을
강한 일관성의 CP(Consistency and Partition tolerance, 일관성과 분단 내성) 시스템으로 바꾸지는
않습니다. 확인응답을 받은 쓰기도 영속화 설정에 따라 페일오버 도중 사라질 수 있습니다.
Kafka
acks=all
min.insync.replicas
min.insync.replicas 는 프로듀서가 acks 를 all 로 두었을 때 쓰기가 성공하려면 리더를
포함해 최소 몇 개의 동기화된 사본이 필요한지를 정합니다. 리더는 동기화된 사본의 집합을
ISR(In-Sync Replicas, 동기화된 사본 집합)로 관리합니다. 메시지는 그 파티션 ISR 의 모든 사본이
자기 로그에 반영했을 때 커밋된 것으로 치고, 커밋된 메시지만 컨슈머에게 나갑니다.
acks=all 이면 ISR 안의 사본 하나하나가 전부 확인응답을 해야 성공입니다. 토픽의 복제 계수가
3 이고 ISR 에 사본 셋이 다 들어 있으면, min.insync.replicas 가 3 보다 작아도 셋 모두가
확인응답을 해야 합니다. 현재 ISR 이 min.insync.replicas 보다 적으면 프로듀서는
NotEnoughReplicas 또는 NotEnoughReplicasAfterAppend 예외를 냅니다.
Amazon S3 크로스 리전 복제
복제는 데이터베이스 밖에서도 같은 이름으로 나타납니다. S3 는 버킷 사이에 객체를 자동으로, 비동기로 복사하는 기능을 복제라고 부릅니다. 복제를 설정한 버킷은 같은 AWS(Amazon Web Services, 아마존 웹 서비스) 계정이 가질 수도 있고 서로 다른 계정이 가질 수도 있습니다. 대상 버킷은 하나여도 되고 여럿이어도 됩니다. 살아 있는 복제에는 두 갈래가 있습니다. 서로 다른 AWS 리전의 버킷으로 복제하는 CRR(Cross-Region Replication, 리전 간 복제)과 같은 리전 안에서 복제하는 SRR(Same-Region Replication, 동일 리전 복제)입니다.
DNS 존 전송
DNS(Domain Name System, 도메인 이름 시스템)도 같은 문제를 풉니다. RFC(Request for Comments) 5936 은 한 존의 권한 있는 네임서버들 사이에서 내용을 일치시키는 표준 수단이 DNS 프로토콜 안에 세 가지 메커니즘으로 있다고 적습니다. AXFR(Authoritative Transfer, 권한 전송)이 그중 하나이고 RFC 1034 와 RFC 1035 가 정의합니다. AXFR 질의를 보내는 쪽이 AXFR 클라이언트, 응답하는 쪽이 AXFR 서버입니다. RFC 5936 은 master · slave · primary · secondary 같은 말이 AXFR 을 정의하는 데는 중요하지 않다고 못 박습니다.
관련 항목
복제를 실제로 구현·채택한 시스템
PostgreSQL · Redis · Kafka · Cassandra · CouchDB · Amazon S3 · DNS
복제가 쓰는 전달 방식
스트리밍 복제 · 논리적 복제 · 존 전송 · AXFR · 크로스 리전 복제
복제를 이루는 구성 요소
WAL · 복제 슬롯 · 베이스 백업 · 복제 로그
복제를 형식으로 세우는 합의 이론
사본 반영을 확인하는 시점
동기 복제 · 비동기 복제 · 반동기 복제 · 쿼럼 · 일관성 수준 · 트랜잭션
복제 지연이 만드는 일관성 문제
복제 지연 · 낡은 읽기 · 결과적 일관성 · 읽기 자기 쓰기 일관성
리더가 사라졌을 때 거치는 절차
복제를 보완하는 분산·백업 기법
복제로 얻는 가용성과 읽기 확장
다른 이름: replication · 리플리케이션 · 레플리케이션