7. 읽기 복제(replication), 복제 지연, 리더 선출, 읽기/쓰기 분리
난이도 상모범답안 — 읽기 복제, 복제 지연, 리더 선출, 읽기/쓰기 분리
난이도: 상
핵심 답변
- 단일 리더 복제: 쓰기는 리더(primary) 1대만 받고, 리더가 변경 로그(WAL/binlog)를 팔로워(replica)들에 전파해 동일 상태로 복제한다. 조회를 여러 replica로 분산하면(읽기/쓰기 분리) 읽기 부하를 수평 확장할 수 있다. 쓰기는 여전히 한 곳이라 쓰기는 확장되지 않는다(그건 샤딩의 영역).
- 동기 vs 비동기 복제:
- 동기: 리더가 (적어도 한) replica의 확인을 받고서야 커밋 성공 응답 → replica 손실 없음(내구성↑)이지만 지연↑, replica가 느리면 쓰기가 막힘.
- 비동기: 리더가 먼저 커밋하고 전파는 뒤따름 → 빠르지만 리더 장애 시 미전파분 손실 가능.
- 흔히 반동기(semi-sync): 최소 1개 replica의 수신만 확인해 절충.
- 복제 지연: replica가 리더보다 뒤처진 시간차. 그동안 replica에서 읽으면 옛 데이터를 본다.
- 리더 선출/페일오버: 리더가 죽으면 남은 노드 중 하나를 새 리더로 승격. 이때 split-brain과 미복제 쓰기 손실을 막는 것이 핵심이며, Raft/Paxos 같은 합의 알고리즘이 "단일 리더 보장 + 커밋된 로그 보존"을 제공한다.
깊이 있는 설명 (왜, 트레이드오프)
복제는 읽기 확장과 가용성을 사는 대신 일관성을 양보한다. 비동기 복제 = 최종 일관성(eventual consistency). replica는 "언젠가" 리더를 따라잡지만 그 사이엔 stale하다. 이 지연은 CAP/PACELC의 "정상 시에도 지연(latency) vs 일관성(consistency)" 트레이드오프(PACELC의 ELC)와 직결된다.
복제 지연이 만드는 구체적 이상현상.
- Read-your-writes 위반: 내가 쓴 직후 replica에서 읽으면 내 변경이 안 보임. UX 사고(방금 한 행동이 사라진 것처럼 보임).
- Monotonic reads 위반: 연속 조회가 서로 다른 replica(지연 정도가 다른)로 가면, 봤던 데이터가 다음 조회에서 사라지는 "시간 역행"이 보임.
- 완화 기법: (1) 자신의 쓰기 직후 일정 시간/세션은 리더에서 읽기(read-from-leader), (2) 세션을 한 replica에 고정(sticky)해 monotonic reads 보장, (3) 쓰기 시점의 로그 위치(LSN/GTID)를 기억하고 그 위치 이상 따라잡은 replica에서만 읽기, (4) replica 지연이 임계치 초과면 라우팅에서 제외.
페일오버의 두 위험.
- 미복제 쓰기 손실: 비동기에서 리더가 죽으면 아직 replica로 안 간 마지막 쓰기들은 사라진다. 옛 리더가 복구돼 돌아오면 그 쓰기가 새 리더와 충돌(divergence)할 수 있어 보통 폐기한다. 결제/재화에는 치명적이라 동기/반동기 또는 멱등·재처리 가능한 설계가 필요.
- Split-brain: 네트워크 분단으로 옛 리더가 살아 있다고 믿고 계속 쓰기를 받는데 새 리더도 쓰기를 받으면, 두 권위가 갈라져 데이터가 분기한다. 방지: 과반수(quorum) 기반 선출(소수파는 리더가 될 수 없음), 펜싱(fencing)/STONITH로 옛 리더 격리, 리스(lease)·에포크(term) 번호로 옛 리더의 쓰기 무효화.
합의 알고리즘(Raft). Raft는 "임기(term)마다 과반 득표한 단일 리더만 존재", "로그 엔트리는 과반에 복제돼야 커밋", "커밋된 엔트리는 이후 리더도 반드시 보존"을 보장한다. 이로써 split-brain(과반은 동시에 둘일 수 없음)과 커밋 손실을 구조적으로 막는다. etcd/Consul/ZooKeeper(ZAB) 등이 이를 코디네이터로 제공한다.
응용/실무 연결 (게임서버에서)
(a) "방금 산 아이템이 안 보임": 구매는 primary에 커밋됐지만 인벤토리 조회가 아직 따라잡지 못한 replica로 가서 stale 결과를 본다. 해결:
- 쓰기 직후 짧은 시간은 primary에서 조회(read-your-writes): 구매 응답 후 N초 또는 "최근 쓰기 세션" 동안 인벤토리는 primary로.
- 쓰기 결과를 클라이언트가 들고 화면을 낙관적으로 갱신(서버 확정값 수신 후 보정).
- 쓰기 LSN을 세션에 저장하고 그 LSN 이상인 replica에서만 읽기.
(b) primary 필수 vs replica 허용 기준
- 반드시 primary(강한 일관성 필요): 재화 잔액 확인 후 차감, 결제 검증, 중복 지급 검사, 구매 직후 본인 인벤토리, 거래 직전 소유권 확인 — stale 값이 정합성·금전 사고로 직결.
- replica 허용(약간 stale 무방): 전체 랭킹/리더보드, 타인 프로필 조회, 도감/통계, 공지·상점 카탈로그, 비실시간 대시보드 — 수 초 지연이 문제되지 않음.
- 기준: "이 조회 결과로 곧장 금전/정합성 결정을 내리는가, 본인이 방금 바꾼 데이터인가" → 예면 primary.
(c) 페일오버 시 미복제 쓰기와 split-brain: 비동기라면 죽기 직전 primary의 미전파 쓰기는 새 primary에 없어 손실될 수 있다(반동기/동기로 위험 축소). split-brain 방지에는 과반 합의 기반 선출 + 펜싱이 필요: 코디네이터(etcd/Consul)가 과반 동의로만 새 리더를 정하고, 옛 리더는 리스 만료/에포크 증가로 쓰기 권한을 잃게 한다(돌아와도 자신이 더는 리더가 아님을 인지하고 강등). 재화 같은 데이터는 멱등 키·이벤트 로그로 손실분을 재처리 가능하게 설계해 두면 안전.
흔한 오답·함정
- "replica를 늘리면 쓰기도 확장된다": 단일 리더에선 쓰기는 한 곳이라 확장 안 됨 → 쓰기 확장은 샤딩.
- 모든 조회를 replica로: 재화/결제 검증까지 replica로 보내면 stale로 인한 사고.
- 비동기 복제인데 손실이 없다고 가정: 페일오버 시 미복제분 손실 가능.
- 단순 헬스체크 기반 자동 승격만으로 split-brain 해결: 과반/펜싱 없이는 두 리더가 생길 수 있다.
- 복제 지연을 0으로 가정: 부하·네트워크에 따라 수 초까지 벌어질 수 있어 일관성 설계가 필요.
꼬리질문 대비
-
Q: 다중 리더(multi-leader)나 리더리스(leaderless, Dynamo형) 복제는 언제 쓰나요? A: 지역 분산 쓰기·고가용성이 필요할 때. 대신 쓰기 충돌 해소(LWW, 벡터 시계, CRDT)와 read/write quorum 같은 추가 복잡도를 감수한다.
-
Q: 동기 복제로 모든 replica 확인을 기다리면 무슨 문제가 있나요? A: 한 replica라도 느리거나 죽으면 쓰기가 막혀 가용성이 급락한다. 그래서 보통 "최소 1대만 확인"하는 반동기로 절충한다.
-
Q: 합의 알고리즘으로 과반(quorum)이 필요한 이유는? A: 노드를 홀수로 두고 과반 동의를 강제하면 동시에 두 과반이 존재할 수 없어 단일 리더가 보장되고, 소수파(분단된 쪽)는 진행을 멈춰 split-brain을 막는다.