15.5 복제 지연·끊김
복제(streaming·logical) 사고는 서서히 자라다 갑자기 터지는 패턴이 흔합니다. lag 모니터링·즉시 대응·재구축 절차를 정리합니다.
lag 측정 — primary 측
SELECT application_name, client_addr, state,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn)) AS sent_lag,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), write_lsn)) AS write_lag,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), flush_lsn)) AS flush_lag,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS replay_lag,
write_lag, flush_lag, replay_lag,
sync_state
FROM pg_stat_replication;| 단계 | 의미 |
|---|---|
sent_lag | primary가 못 보낸 양 — 네트워크 또는 standby 수신 못 함 |
write_lag | standby가 받았지만 안 씀 |
flush_lag | 안 fsync — standby 디스크 느림 |
replay_lag | 받았지만 적용 못 함 — single big tx, 쿼리 충돌 |
lag 원인별 진단
1. sent_lag 큼 → 네트워크 / standby down
# 네트워크
ping standby.example.com
# 또는 standby 호스트 자체 응답 안 함대응: 네트워크 복구합니다. 오랜 시간 끊겨 WAL 잘림 위험 → slot으로 보호 또는 재구축.
2. flush_lag 큼 → standby 디스크 느림
# standby 호스트에서
iostat -xz 1 5
# %util, await 점검대응: 디스크 IO 한계 — 스토리지 점검 또는 인스턴스 upgrade.
3. replay_lag 큼 → single big tx 또는 query conflict
-- standby 측에서 진단
SELECT pid, query, wait_event_type, wait_event,
backend_xmin, now() - xact_start AS age
FROM pg_stat_activity
WHERE state = 'active';| 원인 | 대응 |
|---|---|
| 큰 트랜잭션 (primary의 GB 단위 UPDATE) | 기다림 |
| query conflict (12.3) | max_standby_streaming_delay 조정 |
| hot_standby_feedback 부재 | 인지 |
| disk IO 한계 | 같음 |
standby 끊김
-- primary
SELECT count(*) FROM pg_stat_replication WHERE state = 'streaming';
-- 평소보다 적으면 누군가 끊김
-- 어떤 slot이 inactive
SELECT slot_name, active, restart_lsn
FROM pg_replication_slots
WHERE NOT active;standby 재연결
standby에서:
# 로그 확인
tail -100 /var/lib/pgsql/17/data/log/postgresql-*.log흔한 메시지:
| 메시지 | 원인 |
|---|---|
requested WAL segment ... has already been removed | primary pg_wal 잘림 — slot 없으면 발생 |
could not connect | 네트워크 또는 primary down |
authentication failed | pg_hba·비밀번호 변경 |
replication terminated by primary server | primary 정지 |
WAL 잘려서 재구축 필요
FATAL: could not receive data from WAL stream: ERROR: requested WAL segment ... has already been removedslot 없는 standby가 오래 끊기면 발생합니다. 재구축:
sudo systemctl stop postgresql-17
sudo rm -rf /var/lib/pgsql/17/data/*
sudo -u postgres pg_basebackup -h primary -U repl_user \
-D /var/lib/pgsql/17/data -X stream -P -R --slot=standby1
sudo systemctl start postgresql-17큰 클러스터는 수 시간 걸림 — 평소 슬롯 사용 + max_slot_wal_keep_size로 예방.
logical replication 사고
subscription 멈춤
-- subscriber 측
SELECT subname, received_lsn, latest_end_lsn, latest_end_time
FROM pg_stat_subscription;
SELECT * FROM pg_stat_subscription_stats;
-- apply_error_count, sync_error_count 증가하면 충돌흔한 사고:
| 원인 | 진단 |
|---|---|
| primary key 충돌 | subscriber에 같은 PK row가 이미 |
| DDL 비동기 | publisher만 ALTER, subscriber에 컬럼 없음 |
| sequence 갱신 안 됨 | subscriber에 직접 INSERT 시 충돌 |
| 권한 부족 | subscriber 사용자 권한 |
충돌 후 복구
-- subscriber에서 충돌 row 정리 후
ALTER SUBSCRIPTION sub_orders ENABLE;pg_replication_origin_advance로 LSN 강제 진행도 가능 — 일부 변경 스킵. 데이터 일관성 위험, 신중히 진행합니다.
sync replication에서 standby 죽음
-- primary 트랜잭션이 영원히 commit 안 됨원인: synchronous_standby_names에 지정된 standby가 모두 응답 안 합니다.
대응:
-- 임시 — fallback
ALTER SYSTEM SET synchronous_standby_names = '';
SELECT pg_reload_conf();비동기로 일시 전환 → standby 복구 → 다시 sync 복원. quorum으로 운영(ANY 2 (s1,s2,s3))하면 한 대 죽어도 진행 (12.1).
hot_standby_feedback과 BLOAT
hot_standby_feedback = on인데 standby의 긴 트랜잭션이 있으면 primary vacuum 차단합니다.
-- primary에서
SELECT * FROM pg_stat_replication;
-- backend_xmin 컬럼 — 가장 작은 xmin이 vacuum horizon긴 분석 쿼리가 standby에 떠 있는지 확인합니다.
logical slot xmin
SELECT slot_name, xmin, catalog_xmin, restart_lsn,
pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS lag
FROM pg_replication_slots
WHERE slot_type = 'logical';catalog_xmin이 진행 안 되면 system catalog vacuum 멈춤 → 카탈로그 BLOAT → 모든 쿼리 느려집니다.
대응: subscriber의 적용 상태 확인합니다. 죽어 있으면 죽은 slot 정리합니다.
자동 모니터링 알람
| 지표 | WARN | CRIT |
|---|---|---|
| replay_lag | 1초 | 10초 |
| sent_lag | 100MB | 1GB |
| slot inactive | 즉시 | — |
sync_state = potential (sync standby 없음) | 즉시 | — |
| subscription error | 즉시 | — |
| catalog_xmin stuck | 시간 단위 | — |
정리
- streaming lag 4단계: sent / write / flush / replay
- 가장 흔한 원인: network·standby IO·big tx·query conflict
- WAL 잘리면 standby 재구축 — slot 사용으로 예방
- sync replication은 quorum으로 한 대 죽음 견딤
- logical replication 사고는 DDL 동기화·PK 충돌이 흔함
- catalog_xmin stuck = 카탈로그 BLOAT 폭증
다음 절(15.6)에서는 가장 무서운 사고 — corruption 대응을 봅니다.