12.4 로지컬 복제
Logical replication은 PG 10에서 정식 도입된 행 단위·테이블 단위 복제입니다. WAL을 decode해 SQL-level의 변경을 publisher → subscriber로 보냅니다. 메이저 버전이 달라도 동작, 일부 테이블만 가능, 단방향·다방향 모두. 스키마 변경 무중단 적용·CDC·메이저 업그레이드 무중단 같은 시나리오의 핵심입니다.
physical vs logical 비교
| 측면 | physical (streaming) | logical |
|---|---|---|
| 단위 | 페이지·WAL byte | 행·테이블 |
| subscriber 상태 | 읽기 전용 | 읽기·쓰기 |
| 버전 일치 | primary == standby | 다른 메이저도 OK |
| 일부 테이블만 | 불가 | 가능 |
| 스키마 | 자동 동기화 | 수동 — DDL은 별도 |
| TRUNCATE | 자동 | publication 옵션 |
| LOB 한계 | 없음 | DDL·sequence·LO 미지원 |
활성
publisher
# postgresql.conf
wal_level = logical
max_replication_slots = 10
max_wal_senders = 10restart 필요 (wal_level은 postmaster 컨텍스트).
-- 권한
CREATE ROLE repl_user LOGIN REPLICATION PASSWORD 'secret';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO repl_user;pg_hba.conf:
hostssl app_main repl_user 10.0.0.0/8 scram-sha-256
hostssl replication repl_user 10.0.0.0/8 scram-sha-256publication
-- 전체 DB의 모든 테이블
CREATE PUBLICATION pub_all FOR ALL TABLES;
-- 특정 테이블
CREATE PUBLICATION pub_orders FOR TABLE orders, order_items;
-- 스키마 단위 (PG 15+)
CREATE PUBLICATION pub_app FOR TABLES IN SCHEMA app;
-- 필터링 (PG 15+)
CREATE PUBLICATION pub_kr FOR TABLE orders WHERE (country = 'KR');
-- 컬럼 리스트 (PG 15+)
CREATE PUBLICATION pub_basic FOR TABLE users (id, name);subscriber
-- subscriber 쪽에서 — 같은 이름의 빈 테이블 미리 생성
CREATE TABLE orders (...);
-- subscription
CREATE SUBSCRIPTION sub_orders
CONNECTION 'host=primary.example.com dbname=app_main user=repl_user password=secret sslmode=require'
PUBLICATION pub_orders;이걸로 initial sync(테이블의 현재 내용 복사) + streaming(이후 변경 전달)이 시작됩니다.
동작 흐름
flowchart LR
PG1["publisher<br/>PostgreSQL"]
DEC["walsender +<br/>logical decoding"]
SUB["subscriber<br/>worker"]
APP["apply 적용"]
PG2["subscriber<br/>PostgreSQL"]
PG1 -- "WAL" --> DEC
DEC -- "row 단위 메시지" --> SUB
SUB --> APP --> PG2
classDef pub fill:#ede9fe,stroke:#6d28d9,color:#3b0764
classDef sub fill:#d1fae5,stroke:#047857,color:#064e3b
classDef mid fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
class PG1 pub
class PG2 sub
class DEC,SUB,APP mid
publisher는 logical replication slot을 하나 만들어 이 subscription의 lsn까지 WAL 보존. subscriber가 끊겨도 다시 잇기 가능합니다.
표준 시나리오
1. 메이저 업그레이드 무중단
1. 새 메이저(v18)에 빈 클러스터 + 스키마 dump 복원
2. v17(현 운영) → v18 logical replication 시작
3. initial sync + 따라잡음
4. 짧은 점검 시간에 운영 트래픽을 v18로 전환
5. v17 정지자세한 절차는 18.4.
2. 데이터 분석용 ETL 대체
-- 데이터웨어하우스가 OLTP의 변경을 실시간 받음
CREATE SUBSCRIPTION sub_etl ... PUBLICATION ...;ETL job 대신 상시 동기화합니다.
3. 멀티 마스터 (활성 두 사이트)
PG 코어는 단일 마스터 복제만 — 양방향은 EDB의 BDR이나 PGD 같은 상용 솔루션이 필요합니다. 단순 양방향은 데이터 충돌 위험 크니 신중히 진행합니다.
한계와 함정
DDL은 자동 안 됨
-- publisher
ALTER TABLE orders ADD COLUMN priority int;
-- subscriber는 같은 ALTER가 자동으로 안 됨운영자가 스키마 변경을 양쪽에 같은 순서로 수동 적용해야 합니다. PG 17부터 일부 자동화가 들어왔지만 완전한 DDL 동기화는 아직.
sequence 안 됨
INSERT INTO orders DEFAULT VALUES;
-- publisher의 sequence는 1, 2, 3 ...
-- subscriber에 row는 가지만 subscriber sequence는 갱신 안 됨failover 시 subscriber sequence를 수동으로 보정해야 합니다. PG 16+에서 일부 개선합니다.
충돌 처리
같은 row를 양쪽이 동시에 바꾸면 충돌. 단방향 복제에서는 subscriber의 변경이 있으면 안 됩니다.
ERROR: duplicate key value violates unique constraint "..."ALTER SUBSCRIPTION sub DISABLE 후 수동 정리 → ENABLE.
large object·TRUNCATE·view·시퀀스 ownership
- LO 미지원 — bytea 사용 권장
- TRUNCATE는 PG 11+부터, publication 옵션으로
- view는 정의만 가능, 데이터는 underlying table
- 시퀀스 ownership 안 따라옴
REPLICA IDENTITY
UPDATE/DELETE 시 publisher가 어떤 키로 row를 식별할지 정해야 합니다.
| REPLICA IDENTITY | 의미 |
|---|---|
DEFAULT (기본) | PK 사용 |
FULL | 모든 컬럼을 키로 — PK 없는 테이블 |
USING INDEX | 특정 unique 인덱스 |
NOTHING | UPDATE/DELETE 복제 안 됨 |
PK 있는 테이블은 자동, 없으면:
ALTER TABLE no_pk_table REPLICA IDENTITY FULL;FULL은 모든 컬럼이 WAL에 기록되어 WAL 양 증가합니다. 가능하면 PK 사용합니다.
모니터링
-- publisher 측
SELECT * FROM pg_replication_slots WHERE slot_type = 'logical';
SELECT * FROM pg_stat_replication;
-- subscriber 측
SELECT subname, received_lsn, latest_end_lsn, latest_end_time
FROM pg_stat_subscription;| 신호 | 점검 |
|---|---|
pg_replication_slots.confirmed_flush_lsn 진행 안 됨 | subscriber 적용 멈춤 |
| publisher xmin/catalog_xmin 고정 | vacuum 막힘 → BLOAT |
pg_stat_subscription_stats의 sync_error_count | 충돌 발생 |
운영 안티패턴
| 안티패턴 | 위험 |
|---|---|
| sub가 멈춰 있는데 모르고 둠 | publisher BLOAT 폭증 |
| DDL을 한쪽만 적용 | 다음 DML에서 sub 실패 |
| sequence 갱신 무시 | failover 후 PK 충돌 |
| 같은 row를 양쪽에서 변경 | unique violation 충돌 |
| LOB 컬럼 있는 테이블을 logical로 | 데이터 누락 |
pg_replication_slots가 잡고 있는 catalog_xmin이 멈춰 있으면 운영 클러스터 전체가 vacuum 못 도는 사고로 이어집니다.정리
- logical replication = 행·테이블 단위 복제. 메이저 버전 다름·일부 테이블만 가능
- publication + subscription 모델
- DDL·sequence·LO는 자동 안 됨 — 수동 처리 필요
- REPLICA IDENTITY 설정 (PK 권장, FULL은 비용 큼)
- 표준 시나리오: 메이저 업그레이드 무중단, ETL 대체
- catalog_xmin·sync_error 모니터링 필수
다음 절(12.5)에서는 가장 표준적인 HA 도구 — Patroni를 봅니다.