DB 전문가만 아는 PostgreSQL 복제의 숨겨진 힘
서론 (Introduction)
고가용성 확보, 읽기 전용 쿼리 분산, 또는 데이터베이스 업그레이드를 위해 데이터베이스 복제본을 만들어야 하는 상황은 개발자나 DBA에게 매우 익숙한 시나리오입니다. 많은 이들이 PostgreSQL 복제를 단순히 데이터를 똑같이 복사하는 기능으로 생각하지만, 사실 그 이면에는 훨씬 더 강력하고 놀라운 가능성들이 숨어 있습니다. PostgreSQL은 목적에 따라 완전히 다른 두 가지 방식의 복제 기능을 제공하며, 이 둘의 차이점을 이해하는 것은 시스템 아키텍처를 혁신할 수 있는 열쇠가 됩니다.
이 글에서는 기본적인 설명서를 넘어서, PostgreSQL 복제가 가진 의외의 능력과 간과하기 쉬운 치명적인 함정들을 깊이 있게 파헤쳐 보고자 합니다. 단순히 데이터를 복사하는 것을 넘어, 다운타임 없는 메이저 버전 업그레이드를 실현하고, 심지어 복제본 데이터베이스에 쓰기 작업을 수행하는 방법까지, 여러분이 미처 몰랐을 다섯 가지 중요한 사실을 공개합니다.
1. '복제’는 하나가 아니다: 목적이 완전히 다른 두 가지 방식
PostgreSQL에서 '복제’라는 단어는 하나의 기술을 지칭하지 않습니다. 목적과 동작 방식이 근본적으로 다른 **스트리밍 복제(Streaming Replication)**와 **논리 복제(Logical Replication)**라는 두 가지 방식이 존재합니다.
먼저, 스트리밍 복제는 물리적 복제 방식으로, 데이터베이스의 데이터 블록 레벨에서 변경 사항을 그대로 복사합니다. 이는 데이터베이스 인스턴스 전체를 똑같이 복제하여 고가용성(High Availability)을 위한 완벽한 대기(Standby) 서버를 만드는 데 주로 사용됩니다. 설정이 간단하고 빠르다는 장점이 있지만, 주 서버(Primary)와 대기 서버는 반드시 동일한 운영체제와 PostgreSQL 메이저 버전을 사용해야 한다는 제약이 있습니다.
반면, 논리 복제는 훨씬 유연한 방식으로, 테이블 단위로 변경 사항을 복제합니다. WAL(Write-Ahead Log) 파일을 그대로 복사하는 대신, WAL을 논리적인 변경 내역(예: INSERT, UPDATE 구문)으로 해석하여 전송합니다. 이 덕분에 복제 대상을 세밀하게 제어할 수 있으며, 서로 다른 환경 간에도 데이터를 이전할 수 있습니다.
두 방식의 핵심적인 차이점은 아래 표와 같습니다.
| 비교 항목 (Comparison Item) | 스트리밍 복제 (Streaming Replication) | 논리 복제 (Logical Replication) |
|---|---|---|
| 상/하위 인스턴스 명칭 | 프라이머리(Primary) / 스탠바이(Standby) | 퍼블리셔(Publisher) / 서브스크라이버(Subscriber) |
| 노드 간 전송 데이터 | WAL 자체 | WAL에서 추출된 변경 정보 |
| 복제 대상 | 인스턴스 전체 | 테이블, 스키마 단위 지정 (최대 DB 단위) |
| 상위 인스턴스 수 | 1개만 가능 | 여러 개 설정 가능 |
| 환경 제약 | 동일 OS, 동일 메이저 버전 필수 | OS, 메이저 버전이 달라도 가능 |
| 주요 용도 | 고가용성(HA) 구성, 읽기 부하 분산 | 데이터 마이그레이션, 버전 업그레이드 |
2. 다운타임 없는 메이저 버전 업그레이드가 가능하다
논리 복제가 가진 가장 강력하고 놀라운 활용 사례 중 하나는 바로 무중단(Zero-Downtime) 데이터베이스 메이저 버전 업그레이드입니다. 예를 들어 PostgreSQL 17에서 18+ 버전으로 업그레이드할 때, 서비스 중단 시간을 거의 없앨 수 있습니다.
과정은 의외로 간단합니다. 먼저 새로운 버전의 PostgreSQL 인스턴스를 설치하고, 이를 기존(이전 버전) 인스턴스의 구독자(Subscriber)로 설정합니다. 그러면 기존 데이터베이스가 계속 온라인 상태로 운영되는 동안, 모든 데이터가 백그라운드에서 새로운 인스턴스로 실시간 동기화됩니다. 동기화가 완료되면 애플리케이션의 연결을 새로운 인스턴스로 전환하기만 하면 됩니다. 이 과정에서 발생하는 서비스 중단은 거의 없거나 아주 짧습니다.
이는 긴 유지보수 시간을 확보하기 어려운 미션 크리티컬 시스템의 운영상 큰 난제를 해결해주는 매우 강력한 기능입니다. 하지만 더 놀라운 점은 안전한 롤백 전략까지 가능하다는 것입니다. 데이터 동기화가 완료된 후 트래픽을 전환하기 전에, 역방향으로 새로운 인스턴스에서 기존 인스턴스로의 논리 복제를 추가 설정할 수 있습니다. 이렇게 하면 전환 후 새 시스템에서 예상치 못한 문제가 발생하더라도, 기존 시스템에 모든 변경 사항이 그대로 반영되고 있으므로 즉시 이전 버전으로 안전하게 롤백할 수 있습니다.
설정 또한 아래와 같이 몇 줄의 명령어로 간단하게 시작할 수 있습니다.
-- Old Instance (Publisher)
old=# CREATE PUBLICATION pub FOR ALL TABLES;
-- New Instance (Subscriber)
new=# CREATE SUBSCRIPTION sub1 CONNECTION 'user=postgres port=5432' PUBLICATION pub;
3. 복제본 데이터베이스에 '쓰기’가 가능하다
스트리밍 복제 환경에서 대기(Standby) 서버는 엄격하게 읽기 전용(ERROR: cannot execute CREATE TABLE in a read-only transaction)으로만 동작합니다. 하지만 논리 복제 환경의 구독자(Subscriber) 데이터베이스에는 쓰기 작업이 가능하다는 사실은 매우 직관에 반하는 놀라운 지점입니다.
논리 복제의 구독자는 주 서버에 종속된 읽기 전용 복제본이 아니라, 완전히 독립적인 데이터베이스 인스턴스이기 때문입니다. 이는 스트리밍 복제에서는 상상할 수 없는 다양한 활용 사례를 가능하게 합니다. 예를 들어, 서로 다른 서버에서 각기 다른 쓰기 부하를 처리하여 쓰기 성능을 확장하거나, 심지어 두 데이터베이스가 서로의 변경 사항을 구독하는 양방향(Bi-directional) 복제 시스템을 구축할 수도 있습니다.
하지만 이 강력한 유연성에는 반드시 알아야 할 치명적인 위험이 따릅니다. 바로 데이터 충돌(Data Conflict)이 발생하면 복제가 즉시 중단된다는 점입니다. 예를 들어, 퍼블리셔와 서브스크라이버 양쪽에서 동일한 Primary Key를 가진 행을 INSERT하면 데이터 충돌이 발생하고, PostgreSQL은 어느 쪽 데이터를 우선해야 할지 판단할 수 없어 복제 프로세스를 멈춰버립니다. 따라서 서브스크라이버에 쓰기를 허용하는 아키텍처를 설계할 때는 데이터 충돌을 애플리케이션 레벨에서 반드시 방지할 수 있는 명확한 전략이 필요합니다.
4. 방치된 설정 하나가 메인 데이터베이스를 멈추게 할 수 있다
PostgreSQL 복제를 처음 다루는 팀이 가장 흔하게 겪는 위험한 함정 중 하나는 바로 복제 슬롯(Replication Slots) 관리입니다. 복제 슬롯이란, "이 WAL 파일은 아직 구독자가 가져가지 않았으니, 삭제하거나 재활용하지 마시오"라고 주 서버(Publisher)에게 명시적으로 알려주는 포인터와 같은 안전장치입니다.
하지만 이 안전장치가 치명적인 문제를 일으킬 수 있습니다. 만약 구독자 서버가 영구적으로 오프라인 상태가 되거나 제거되었는데, 해당 서버에 할당된 복제 슬롯을 삭제하지 않고 방치하면 어떻게 될까요? 주 서버는 해당 슬롯이 존재하는 한, 구독자가 언젠가 WAL 파일을 가져갈 것이라 믿고 관련 WAL 파일을 영원히 삭제하지 않고 쌓아두게 됩니다. 결국 디스크 공간이 가득 차게 되고, 더 이상 로그를 기록할 수 없게 된 주 데이터베이스는 그대로 멈춰버립니다.
주의: 불필요한 복제 슬롯을 방치하면 WAL 파일이 삭제되지 않고 누적되어, 결국 디스크를 가득 채우고 메인 데이터베이스의 장애를 유발할 수 있습니다.
따라서 복제 연결을 해제할 때는 반드시 관련 복제 슬롯도 함께 정리하는 것을 잊지 말아야 합니다.
5. 전체가 아닌, 특정 '행’이나 '열’만 복제할 수 있다
논리 복제는 단순히 테이블 전체를 복사하는 것을 넘어, 놀라울 정도로 세밀한 제어 기능을 제공합니다. 특정 조건에 맞는 데이터만 선택적으로 복제하는 것이 가능합니다.
예를 들어, 두 가지 강력한 필터링 기능을 활용할 수 있습니다.
-
행 필터링 (Row Filtering): 특정 조건을 만족하는 행들만 복제할 수 있습니다. 예를 들어 직원 테이블에서 급여가 100을 초과하는 직원의 정보만 복제하는 것이 가능합니다.
-
열 필터링 (Column Filtering): 테이블의 모든 열이 아닌, 특정 열만 선택하여 복제할 수 있습니다. 예를 들어, 직원 테이블에서 다른 개인 정보는 제외하고
salary열만 복제할 수 있습니다.
아래 코드는 이러한 개념을 실제로 구현한 예시입니다.
-- Replicate only rows where salary is greater than 100
CREATE PUBLICATION filter_pub FOR TABLE employee WHERE (salary > 100);
-- Replicate only the salary column from the employee table
CREATE PUBLICATION list_pub FOR TABLE employee (salary);
이러한 정밀한 제어 기능 덕분에, 모든 데이터를 노출하지 않고도 분석, 리포팅 또는 다른 서비스와의 연동을 위해 필요한 데이터만 안전하게 분리하여 특화된 데이터 서브셋을 만들 수 있습니다.
결론 (Conclusion)
PostgreSQL의 복제 기능은 단순히 데이터를 복사하는 도구가 아니라, 시스템의 목적에 맞춰 매우 다양하게 활용될 수 있는 강력하고 정교한 기능 모음입니다. 철저한 데이터 일관성과 고가용성이 목표라면 스트리밍 복제가, 무중단 업그레이드나 유연한 데이터 통합이 필요하다면 논리 복제가 해답이 될 수 있습니다. 어떤 방식을 선택할지는 전적으로 달성하고자 하는 목표에 달려있습니다.
당신의 데이터베이스 아키텍처는 PostgreSQL 복제의 숨겨진 기능들을 통해 어떻게 혁신될 수 있을까요?