스트리밍 복제 — Primary/Standby 구축
실시간 복제와 핫 스탠바이. 장애 복구의 표준 아키텍처.
한 대만 굴리면 죽을 때 끝납니다. 운영 DB 는 거의 항상 2대 이상 — Primary + Standby. Primary 가 받은 쓰기를 WAL 로 Standby 에 실시간 전송, Primary 가 죽으면 Standby 가 Primary 가 됩니다. 21편은 그 표준 아키텍처를 처음부터 끝까지.
왜 복제? — 3가지 이유
- 고가용성(HA) — Primary 죽어도 Standby 가 즉시 대체.
- 읽기 분산 — Standby 에 SELECT 보내 부하 분산 (hot standby).
- 지리적 분산 — 다른 리전에 Standby 두어 재해 대비.
WAL 기반 복제 — 한 줄 원리
WAL(Write-Ahead Log). Postgres 는 모든 변경을 디스크 데이터 파일에 쓰기 전에 WAL 에 먼저 기록 (durability 보장). 복제는 그 WAL 을 Standby 에 그대로 전달해 같은 변경을 다시 적용. 그래서 두 노드의 상태가 동일.
Primary 설정 — postgresql.conf
# Primary
wal_level = replica
max_wal_senders = 10 # 최대 standby 수 + 여유
wal_keep_size = 1GB # standby 가 따라잡을 시간 여유
synchronous_commit = on # 또는 remote_apply (동기 시)
# pg_hba.conf — replicator 사용자 허용
host replication replicator 10.0.0.0/24 scram-sha-256
-- Primary 에서 replicator 역할 생성
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD '...';
-- replication slot 만들기 (권장)
SELECT pg_create_physical_replication_slot('standby_1');
-- WAL 이 standby 가 받기 전까지는 삭제되지 않음 → 안전
Standby 부트스트랩 — pg_basebackup
# Standby 노드에서, 빈 디렉터리에
sudo systemctl stop postgresql
sudo -u postgres rm -rf /var/lib/postgresql/16/main
sudo -u postgres pg_basebackup \
-h primary.example.com \
-U replicator \
-D /var/lib/postgresql/16/main \
-Fp -Xs -P -R \
-S standby_1
# -R : 자동으로 standby.signal + primary_conninfo 생성
# -S standby_1 : 미리 만든 replication slot 연결
sudo systemctl start postgresql
postgresql.auto.conf 자동 생성 결과
# pg_basebackup -R 가 만들어 줌
primary_conninfo = 'host=primary.example.com user=replicator password=... port=5432'
primary_slot_name = 'standby_1'
# standby.signal 빈 파일이 있으면 Postgres 가 standby 모드로 시작
복제 상태 확인
-- Primary 에서
SELECT pid, usename, application_name, client_addr,
state, sync_state, sent_lsn, write_lsn, flush_lsn, replay_lsn,
pg_size_pretty(pg_wal_lsn_diff(sent_lsn, replay_lsn)) AS lag
FROM pg_stat_replication;
-- Standby 에서
SELECT pg_is_in_recovery(); -- true 면 standby
SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();
hot standby — 읽기 분산
# Standby 의 postgresql.conf
hot_standby = on # standby 에서 SELECT 허용 (PG 11+ 는 기본)
max_standby_streaming_delay = 30s # 충돌 시 standby 가 대기할 최대 시간
-- 애플리케이션 — 읽기는 standby, 쓰기는 primary
const writeDb = new Pool({ host: "primary.example.com" });
const readDb = new Pool({ host: "standby.example.com" });
await writeDb.query("INSERT ..."); // primary
await readDb.query("SELECT ..."); // standby (살짝 옛 데이터 가능)
읽기 일관성 함정. Standby 는 보통 수십 ms 뒤처짐. "방금 INSERT 한 것을 같은 요청에서 SELECT" 면 standby 에 없을 수도. 본인이 방금 쓴 데이터는 primary 에서 읽기가 안전한 패턴 (read-your-writes).
동기 vs 비동기
| 비동기 (기본) | 동기 | |
|---|---|---|
| commit 응답 | primary local 디스크 | standby 도 받음 |
| 데이터 손실 위험 | primary 갑자기 죽으면 마지막 몇 ms 손실 가능 | 없음 |
| 쓰기 속도 | 빠름 | 네트워크 RT 만큼 느림 |
| 설정 | 기본 | synchronous_standby_names = '*' |
# 동기 복제
synchronous_commit = on
synchronous_standby_names = 'ANY 1 (s1, s2, s3)' -- 셋 중 하나만 응답하면 OK
# 가장 강한 — remote_apply (standby 가 replay 까지 완료)
synchronous_commit = remote_apply
failover — Primary 죽었을 때
# Standby 를 primary 로 승격
sudo -u postgres pg_ctl promote -D /var/lib/postgresql/16/main
# 또는 SQL (PG 12+)
SELECT pg_promote();
# 결과 — standby.signal 제거, 쓰기 받기 시작
실전 failover. 수동은 위험·느림. patroni + etcd(또는 consul) 가 표준 — 자동 감지 + 안전한 승격 + split-brain 방지. 클라우드는 RDS·Aurora 가 같은 일을 관리형으로.
아키텍처 레퍼런스
# 작은 규모
[Primary] --(WAL stream)--> [Standby]
^ |
| v
App writes App reads (hot standby)
# 중간 규모 (자동 failover)
[Patroni cluster: Primary + Standby x N]
^ ^
| |
[HAProxy or pgBouncer for connection routing]
^
|
[App]
# 대규모 (관리형 추천)
[AWS RDS/Aurora] — 자동 백업·자동 failover·read replica
logical replication — 부분 복제
-- 옵션 — 전체가 아닌 특정 테이블만 복제
-- PG 10+ 표준
-- Primary
ALTER SYSTEM SET wal_level = 'logical';
CREATE PUBLICATION my_pub FOR TABLE users, orders;
-- Subscriber
CREATE SUBSCRIPTION my_sub
CONNECTION 'host=primary user=...'
PUBLICATION my_pub;
스트리밍 복제는 전체 클러스터 복제, logical 은 테이블 단위·다른 메이저 버전 간·다른 스키마 변환 가능. 마이그레이션·CDC 에 자주 사용.
모니터링 체크리스트
pg_stat_replication의 lag — 1초 이상이면 경고.- Standby 의
pg_last_wal_replay_lsn정지 — 어딘가 막혔음. - replication slot 의
active = false가 길면 — WAL 이 쌓여 디스크 폭주. - 주기적 failover 훈련 — 진짜 사고 났을 때 당황 안 함.
22편 — 파티셔닝 (RANGE·LIST·HASH)
큰 테이블을 잘게 — 시계열·다국가 데이터의 표준.
이전: 20편 백업 · 현재: 21편 (고급) · 다음 → 22편 파티셔닝 · 진행: 21/24