학습 목표
Primary-Replica(Leader-Follower) 토폴로지에서 쓰기·읽기가 어떻게 나뉘는지 설명할 수 있다.
동기·비동기·반동기 복제의 지연·가용성·일관성 트레이드오프를 설명할 수 있다.
Replication lag가 stale read·read-your-writes 실패로 이어지는 방식을 설명할 수 있다.
WAL·binlog 기반 물리·논리 복제 개념을 설명할 수 있다.
Failover·split-brain 위험과 운영에서 점검할 항목을 설명할 수 있다.
문제 상황
- 단일 Primary에 읽기가 몰려 CPU·I/O 100%
SELECT만 10배 늘었는데 쓰기 DB 직접 조회
- Replica에 방금 쓴 주문이 안 보인다
- Primary COMMIT 직후 Replica
SELECT— lag 2초
- Primary COMMIT 직후 Replica
- 동기 복제로 RPO=0 목표 → Primary COMMIT 느려짐
- Replica 1대 다운 시 쓰기 전체 멈춤
- Primary 장애 → 수동 promote — 두 Primary 동시 기동 split-brain
wal_level=minimal인데 streaming replica 추가 실패- WAL 용도·설정 부족
Part 3 MVCC·WAL까지는 한 노드 안이었다. Part 4 분산과 확장 — 여러 노드에 같은 데이터를 전파한다.
1. Primary-Replica
복제(Replication) — Primary의 변경을 Replica에 복사해 데이터 사본을 유지.

| 역할 | 트래픽 | 예 |
|---|---|---|
| Primary | 쓰기 + (선택) 읽기 | INSERT·UPDATE·DELETE |
| Replica | 읽기 (기본) | report, search, analytics |
| Standby | 복제만 — promote 대기 | DR, failover |
Client write → Primary → WAL / binlog stream → Replica apply
Client read → Replica (optional routing)
| 토폴로지 | 설명 |
|---|---|
| Single-leader | 쓰기 Primary 하나 — 흔함 |
| Multi-leader | 쓰기 여러 — 충돌 해결 필요 (지역별) |
| Leaderless | quorum read/write — Cassandra 계열 |
- 앞서 WAL — 복제 스트림의 원천 (PostgreSQL)
- 읽기 확장 — Replica 추가로 read throughput 선형에 가깝게 (lag 주의)
2. 복제 방식
| 방식 | 내용 | 예 |
|---|---|---|
| Physical (streaming) | WAL 바이트 그대로 | PostgreSQL streaming |
| Logical | 행·DDL 이벤트 | PG logical replication, Debezium |
| Statement | SQL 재실행 | MySQL statement (비결정적 위험) |
| Row-based binlog | 행 변경 이벤트 | MySQL InnoDB 기본 |
-- PostgreSQL streaming (개념)
-- primary: wal_level = replica (or logical)
-- replica: primary_conninfo, recovery mode
SHOW wal_level;
SELECT pg_is_in_recovery(); -- false on primary, true on replica
-- MySQL replica status (발췌)
SHOW REPLICA STATUS\G
-- Seconds_Behind_Source, Relay_Log_Pos
- Physical — 전체 DB 동일 (버전·extension 맞춤)
- Logical — 테이블 단위·다른 PG 버전·CDC (CDC 편)
3. 동기 vs 비동기

| 모드 | COMMIT 시점 | RPO | 가용성 |
|---|---|---|---|
| Async | Primary WAL local flush 후 | lag만큼 손실 가능 | Replica down 무관 |
| Sync | Replica apply·ack 후 | 0 (해당 replica) | Replica down → 쓰기 block |
| Semi-sync | 최소 1 replica ack | 제한적 0 | 일부 replica 필수 |
Async:
Primary COMMIT → client OK → (later) replica catch up
Sync (synchronous_commit = remote_apply):
Primary → WAL send → Replica flush → ack → Primary COMMIT → client OK
| PostgreSQL | MySQL |
|---|---|
synchronous_commit |
semi-sync plugin |
synchronous_standby_names |
rpl_semi_sync_* |
| quorum — N 중 K ack | after_sync / after_commit |
- CAP 관점 — 네트워크 분단 시 sync는 가용성 vs 일관성 선택
- Cross-region replica — RTT만으로 COMMIT 지연 — async 흔함
4. Replication lag
Replication lag — Replica가 Primary 변경을 적용하기까지 지연.

| lag 원인 | |
|---|---|
| 네트워크 | cross-AZ·cross-region |
| Replica 부하 | heavy read → apply 지연 |
| 대용량 트랜잭션 | bulk load — WAL 한꺼번에 |
| 단일 apply thread | MySQL SQL thread 병목 |
| Vacuum·DDL | replica 충돌·lock |
-- PostgreSQL lag (examples)
SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();
SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp())) AS lag_sec;
-- MySQL
SHOW REPLICA STATUS\G
-- Seconds_Behind_Source (estimate)
| 앱 증상 | |
|---|---|
| Read-your-writes 실패 | 쓰기 Primary → 읽기 Replica 즉시 |
| Stale dashboard | report 몇 분 전 |
| Unique 충돌 | failover 후 순서 꼬임 |
앱 대응
| 패턴 | |
|---|---|
| 쓰기 후 읽기 Primary | 주문 생성 → detail Primary |
| Session stickiness | user → 같은 replica + lag 허용 |
| Lag threshold routing | lag > N초 → Primary로 read |
| Causal / version | write_version 컬럼 — client min version |
예: code/read-replica-routing.example
- 모니터링 — lag 알람 (Observability)
- 비동기 전제 — 비즈니스가 몇 초 stale 허용하는지 명시
5. 읽기 확장
| 단일 Primary | + N Replica | |
|---|---|---|
| Write | 한 노드 한계 | 동일 (Primary) |
| Read | Primary 공유 | 분산 |
| 일관성 | 강함 | lag 약함 |
| 비용 | replica 인스턴스 |
┌─ Replica 1 ← report
Primary ─┼─ Replica 2 ← search
└─ Replica 3 ← analytics
| 주의 | |
|---|---|
| Hot spot | Primary 쓰기는 못 나눔 |
| Replica lag | read 품질 |
| Connection pool | replica 마다 pool |
| Temp table·sequence | replica 쓰기 ❌ |
- Connection pool·ORM — read/write datasource 분리 (실무 편)
- Replica too many — Primary WAL send 부하
6. Failover 개요
Failover — Primary 장애 시 Replica를 새 Primary로 승격.
| 단계 | |
|---|---|
| Detect | health check·heartbeat timeout |
| Elect | 하나만 promote — quorum·orchestrator |
| Promote | pg_promote() · CHANGE REPLICATION SOURCE |
| Rewire | DNS·VIP·proxy → 새 Primary |
| Old primary | fencing — split-brain 방지 |
| 위험 | |
|---|---|
| Split-brain | 두 Primary — 양방향 쓰기 |
| Data loss | async — 미복제 WAL |
| Sequence·GTID gap | promote 후 중복 id |
| Brain split auto-failover | flapping — 수동 runbook 병행 |
| 도구 | |
|---|---|
| Patroni | PG HA — etcd leader |
| Orchestrator | MySQL topology |
| RDS / Cloud SQL | 관리형 failover |
| Pgpool / HAProxy | routing — promote와 별개 |
- Failover ≠ load balancing — 쓰기 대상 하나
- DR drill — 분기마다 promote 연습
- 앞서 WAL 아카이브 — PITR + replica 조합
7. 체크리스트
| 항목 | 확인 |
|---|---|
wal_level / binlog |
streaming·logical 충족 |
| Sync policy | RPO vs latency 합의 |
| Lag alert | seconds threshold |
| App routing | write 후 read Primary? |
| Failover | 자동 vs 수동 runbook |
| Split-brain | fencing·STONITH |
| Replica read-only | 쓰기 실수 차단 |
예시 SQL·설정: code/replication-notes.sql
8. 정리
- Primary-Replica — 쓰기 한 곳, 읽기 확장
- Async — 빠른 COMMIT, lag·손실 tradeoff · Sync — RPO ↓, 가용성 ↓
- Replication lag — stale read — 앱 routing·version 필요
- WAL/binlog — physical 스트림 vs logical CDC
- Failover — split-brain·data loss — quorum·drill
다음에 다룰 것
- 파티셔닝과 샤딩
- range/hash 파티션, 샤드 키, 핫스팟
해당 내용은 Database System Concepts, 7/E (Avraham Silberschatz, Henry F. Korth, S. Sudarshan), Designing Data-Intensive Applications (Martin Kleppmann) 의 내용을 기반으로 합니다.
'데이터베이스 따라잡기' 카테고리의 다른 글
| 분산 트랜잭션 개요 (0) | 2026.07.03 |
|---|---|
| 파티셔닝과 샤딩 (0) | 2026.07.02 |
| MVCC (0) | 2026.06.30 |
| 로깅과 복구 (0) | 2026.06.29 |
| 데드락과 타임아웃 (0) | 2026.06.28 |