3 분 소요

PostgreSQL의 복제는 크게 둘로 나뉩니다. 물리 복제(Streaming Replication)는 WAL 블록 단위로 바이트 레벨 동기화하며, 전체 클러스터를 그대로 복사하므로 버전·아키텍처가 같아야 합니다. 논리 복제(Logical Replication)는 WAL을 디코딩해 INSERT/UPDATE/DELETE 수준으로 전달하므로 버전·스키마가 달라도 작동하며, 일부 테이블만 복제하거나 서로 다른 샤드를 합치는 용도로 활용할 수 있습니다. PostgreSQL 17부터는 논리 복제가 failover slot과 시퀀스 복제를 지원하면서 HA 구성에서도 더 적극적으로 쓸 수 있게 됐습니다.

PostgreSQL 17에서 달라진 점

  • failover slot: 논리 슬롯이 스탠바이로 자동 승계되어, 프라이머리 장애 후에도 구독이 깨지지 않음
  • 양방향 복제(bi-directional) 공식 지원: origin 필터와 replication identity 확장으로 구성이 단순화
  • pg_createsubscriber: 물리 스탠바이를 논리 구독자로 변환하는 공식 도구
  • 컬럼 단위 필터링 고도화: UPDATE 시 특정 컬럼만 전송
  • pg_logical_emit_message: 애플리케이션 메시지를 WAL에 주입해 논리 스트림으로 전달

1. 전제 조건

프라이머리(Publisher)와 스탠바이(Subscriber) 모두 다음 설정이 필요합니다.

# postgresql.conf (Publisher)
wal_level = logical               # 논리 디코딩 활성화
max_replication_slots = 10        # 슬롯 예약 수
max_wal_senders = 10              # wal sender 프로세스 수
wal_keep_size = 1GB               # 슬롯 없을 때 WAL 보관량

# postgresql.conf (Subscriber)
max_logical_replication_workers = 4
max_worker_processes = 8
# pg_hba.conf (Publisher)
host    replication     replicator     10.0.0.0/24    scram-sha-256
host    appdb           replicator     10.0.0.0/24    scram-sha-256

전용 복제 계정을 만들고 REPLICATION 권한을 부여합니다.

-- Publisher
CREATE ROLE replicator WITH LOGIN REPLICATION PASSWORD 'StrongPass123!';
GRANT CONNECT ON DATABASE appdb TO replicator;
\c appdb
GRANT USAGE ON SCHEMA public TO replicator;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO replicator;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT ON TABLES TO replicator;

2. Publication 만들기

Publication은 “어떤 테이블의 어떤 변경을 내보낼지” 정의합니다.

-- Publisher
-- 전체 테이블 복제
CREATE PUBLICATION pub_all FOR ALL TABLES;

-- 일부 테이블만
CREATE PUBLICATION pub_core
  FOR TABLE users, orders, payments
  WITH (publish = 'insert, update, delete');

-- 행 필터 (WHERE 절)
CREATE PUBLICATION pub_kr_users
  FOR TABLE users WHERE (country_code = 'KR');

-- 컬럼 필터
CREATE PUBLICATION pub_users_min
  FOR TABLE users (id, email, updated_at);

3. Subscription 생성

구독자 측에서는 동일한 스키마가 먼저 존재해야 합니다. 최초 스키마는 pg_dump -s로 복사합니다.

# Publisher에서 스키마만 덤프
pg_dump -h pub-host -U postgres -s appdb > schema.sql

# Subscriber에 적용
psql -h sub-host -U postgres -d appdb -f schema.sql

이후 구독을 생성합니다.

-- Subscriber
CREATE SUBSCRIPTION sub_core
  CONNECTION 'host=pub-host port=5432 dbname=appdb user=replicator password=StrongPass123!'
  PUBLICATION pub_core
  WITH (
    copy_data     = true,     -- 최초 스냅샷 복사
    create_slot   = true,     -- Publisher에 슬롯 자동 생성
    slot_name     = 'sub_core_slot',
    failover      = true,     -- PG17: 슬롯을 스탠바이로 승계
    synchronous_commit = 'off'
  );

초기 동기화가 끝나면 WAL 기반 실시간 스트리밍으로 전환됩니다.

4. 진행 상태 모니터링

-- Publisher: 슬롯·lag 확인
SELECT slot_name, active, restart_lsn,
       confirmed_flush_lsn,
       pg_size_pretty(
         pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)
       ) AS lag
FROM pg_replication_slots;

-- Publisher: WAL sender 상태
SELECT application_name, state, sync_state,
       write_lag, flush_lag, replay_lag
FROM pg_stat_replication;

-- Subscriber: 구독별 수신 상태
SELECT subname, received_lsn, latest_end_lsn,
       latest_end_time, last_msg_send_time
FROM pg_stat_subscription;

pg_stat_replication.replay_lag가 꾸준히 증가하면 구독자 측 I/O·CPU가 병목입니다. pg_replication_slots.lag가 수 GB 이상으로 쌓이면 Publisher 디스크가 고갈될 수 있으니 경보를 꼭 걸어 둡니다.

5. 장애·재동기화

구독이 오래 끊어져 WAL이 삭제됐다면 ALTER SUBSCRIPTION sub_core REFRESH PUBLICATION으로는 복구되지 않습니다. 이때는 구독을 지우고 대상 테이블을 TRUNCATE 후 재생성합니다.

-- Subscriber
ALTER SUBSCRIPTION sub_core DISABLE;
ALTER SUBSCRIPTION sub_core SET (slot_name = NONE);
DROP SUBSCRIPTION sub_core;

-- 대상 테이블 비우기
TRUNCATE users, orders, payments;

-- 재생성
CREATE SUBSCRIPTION sub_core ...;

6. 주의 사항 체크리스트

  • Primary Key 필수: UPDATE·DELETE를 복제하려면 REPLICA IDENTITY가 필요합니다. 대부분 PK로 충분하지만, PK 없는 테이블은 ALTER TABLE ... REPLICA IDENTITY FULL이 필요하며 성능 비용이 큽니다.
  • DDL은 복제되지 않음: CREATE TABLE, ALTER TABLE 등은 구독자에게 전달되지 않으므로 양쪽에 수동 반영하거나 ddl-trigger 확장을 사용합니다.
  • 시퀀스 값: PG17부터 시퀀스 복제가 지원되지만 기본값은 꺼짐(publish_via_partition_root = false). 필요 시 별도 옵션 지정.
  • TOAST 컬럼: UPDATE 시 변경되지 않은 TOAST 값은 전송되지 않으므로 구독자가 값을 못 받는 경우가 있습니다. REPLICA IDENTITY FULL로 해결하거나 값이 자주 바뀐다면 복제 외 동기화를 별도로 둡니다.

7. pg_createsubscriber로 스탠바이 전환

기존 물리 스탠바이를 논리 구독자로 빠르게 변환할 수 있습니다.

# 스탠바이 정지 후 실행
pg_ctl stop -D /var/lib/pgsql/17/data -m fast

pg_createsubscriber \
  --pgdata=/var/lib/pgsql/17/data \
  --publisher-server='host=pub-host dbname=appdb user=postgres' \
  --publication=pub_core \
  --subscription=sub_core \
  --database=appdb

pg_ctl start -D /var/lib/pgsql/17/data

전체 데이터를 재복사하지 않고 기존 물리 복제 상태에서 논리 복제로 전환하므로, 수 TB급 환경에서도 다운타임을 분 단위로 제한할 수 있습니다.


논리 복제는 마이그레이션(버전 업그레이드, 리전 이동), 읽기 전용 분석 DB 운영, 샤딩된 데이터를 통합 리포팅 DB로 수집하는 시나리오에 특히 강점이 있습니다. 물리 복제와 배타적 관계가 아니므로 HA는 물리 복제로, 데이터 수집·마이그레이션은 논리 복제로 쓰는 하이브리드 구성이 실무에서 흔합니다.

댓글남기기