12.1 스트리밍 복제
Streaming replication은 PostgreSQL의 표준 물리 복제입니다. primary가 만든 WAL을 실시간으로 standby에 전달해 같은 상태의 복제본을 유지합니다. HA·읽기 부하 분산·DR(재해 복구)·백업 보조 등 운영의 기반입니다.
구조
flowchart LR
CLI["client"]
PG1["primary"]
WS["walsender"]
WR["walreceiver"]
PG2["standby (read-only)"]
CLI2["read clients"]
CLI -->|read·write| PG1
PG1 -->|WAL 변경 발생| WS
WS -->|TCP 스트림| WR
WR --> PG2
CLI2 -->|read only| PG2
classDef pri fill:#ede9fe,stroke:#6d28d9,color:#3b0764,stroke-width:2px
classDef std fill:#d1fae5,stroke:#047857,color:#064e3b
classDef mid fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
class PG1 pri
class PG2 std
class WS,WR mid
- primary에서 walsender가 standby의 walreceiver에 WAL을 보냄
- standby의 startup process가 받은 WAL을 재생해 상태 유지
- standby는 read-only —
hot_standby = on일 때 SELECT 가능
기본 설정
primary
# postgresql.conf
wal_level = replica # 최소
max_wal_senders = 10
wal_keep_size = 1GB # 슬롯 없는 standby의 안전망
max_replication_slots = 10# pg_hba.conf
hostssl replication repl_user 10.0.0.0/8 scram-sha-256CREATE ROLE repl_user WITH REPLICATION LOGIN PASSWORD 'secret';standby 초기화
primary의 데이터를 복사한 뒤 standby로 띄움.
# standby 호스트에서
sudo -u postgres pg_basebackup \
-h primary.example.com -U repl_user \
-D /var/lib/pgsql/17/data \
-X stream -P -R --slot=standby1
# 자동으로:
# - standby.signal 파일 생성
# - postgresql.auto.conf에 primary_conninfo, primary_slot_name 추가sudo systemctl start postgresql-17두 가지 동기 모드
| 모드 | synchronous_commit |
|---|---|
| 비동기 (async) | local 또는 off — 기본 |
| 동기 (sync) | on/remote_write/remote_apply + synchronous_standby_names |
비동기
# primary
synchronous_commit = localprimary commit이 standby 응답을 안 기다림. 빠르지만 primary 손실 시 마지막 N초 트랜잭션 손실 가능합니다.
동기
# primary
synchronous_commit = on
synchronous_standby_names = 'ANY 2 (s1, s2, s3)' # 3대 중 2대 응답 (quorum)| 표현 | 의미 |
|---|---|
FIRST 1 (s1, s2) | 우선순위 — s1 응답 우선, 안 되면 s2 |
ANY 1 (s1, s2) | 둘 중 하나 응답 (quorum) |
ANY 2 (s1, s2, s3) | 셋 중 둘 응답 |
'*' | 모든 standby |
Quorum 권장: 2 of 3 형태. 한 standby가 죽어도 primary가 계속 동작합니다. FIRST 1 (s1)처럼 단일 standby에 의존하면 그 standby가 죽을 때 primary commit이 영원히 멈춥니다.
synchronous_commit 단계별
| 값 | primary fsync | standby write | standby flush | standby apply |
|---|---|---|---|---|
off | 비동기 | — | — | — |
local | 동기 | — | — | — |
remote_write | 동기 | 동기 | — | — |
on | 동기 | 동기 | 동기 | — |
remote_apply | 동기 | 동기 | 동기 | 동기 |
remote_apply는 standby에서 query까지 같은 결과 보장 — 비용 큽니다. 대부분 운영은 on 또는 remote_write.
standby 동작 확인 — primary 측
SELECT pid, application_name, client_addr, state,
sent_lsn, write_lsn, flush_lsn, replay_lsn,
write_lag, flush_lag, replay_lag, sync_state
FROM pg_stat_replication;| 컬럼 | 의미 |
|---|---|
state | streaming 정상 |
sent_lsn | primary가 보낸 위치 |
write_lsn | standby가 받아쓴 위치 |
flush_lsn | standby가 fsync한 위치 |
replay_lsn | standby가 재생한 위치 |
*_lag | 각 단계 지연 시간 |
sync_state | sync / async / quorum / potential |
replay_lag가 분 단위로 자라면 standby 부하 또는 단일 트랜잭션 적용 지연.
standby 측 확인
SELECT pg_is_in_recovery(),
pg_last_wal_receive_lsn(),
pg_last_wal_replay_lsn(),
pg_last_xact_replay_timestamp();pg_last_wal_receive_lsn 과 pg_last_wal_replay_lsn의 차이가 재생 lag.
cascading replication
standby가 또 다른 standby에 WAL을 보낼 수 있습니다.
flowchart LR
P["primary"] --> S1["standby 1<br/>(streaming)"]
S1 --> S2["standby 2<br/>(cascading)"]
S1 --> S3["standby 3"]
classDef p fill:#ede9fe,stroke:#6d28d9,color:#3b0764
classDef s fill:#d1fae5,stroke:#047857,color:#064e3b
class P p
class S1,S2,S3 s
장점: primary 부하 ↓. 단점: S1 장애 시 S2·S3가 같이 끊김.
운영 시 주의
| 주의 | 메모 |
|---|---|
primary의 wal_keep_size가 너무 작음 + 슬롯 없음 | standby 연결 끊기면 WAL 잘림 → 재구축 필요 |
슬롯이 있으면 안전, but primary pg_wal/ 가득 차는 위험 | 모니터링 필수 |
| 동기 단일 standby | 그 standby 장애 시 primary 정지 — quorum으로 |
| standby가 매우 뒤처짐 | hot_standby_feedback·single big tx 검토 |
| timeline 불일치 | 옛 standby를 새 primary에 붙일 때 |
비동기 standby의 데이터 손실
primary가 죽었을 때 비동기 standby가 가지지 못한 WAL이 있을 수 있습니다. 그 standby를 promote하면 그 분량의 트랜잭션은 사라집니다. 동기 복제만이 commit 응답 = 손실 없음을 보장.
운영 패턴:
- 동기 1대(같은 데이터센터) + 비동기 1~2대(DR 리전) = 손실 방지 + DR 모두
정리
- streaming replication = WAL 실시간 전송, standby가 같은 상태 유지
- 비동기는 빠르지만 손실 가능, 동기는 안전하지만 비용
- quorum 동기(
ANY 2 (s1, s2, s3))가 운영 표준 - 진단:
pg_stat_replication(primary),pg_is_in_recovery+replay_lsn(standby) - cascading으로 primary 부하 분산 가능
- 슬롯 없는 standby는
wal_keep_size가 안전망
다음 절(12.2)에서는 standby 연결 끊김에도 WAL을 보장하는 복제 슬롯을 봅니다.