PostgreSQL 논리적 복제를 통한 데이터베이스 간 원활한 데이터 동기화
Olivia Novak
Dev Intern · Leapcell

소개
오늘날 데이터 중심의 세계에서 애플리케이션은 종종 여러 데이터베이스에 걸쳐 있어 데이터를 일관되게 유지하고 거의 실시간 업데이트를 제공하는 효율적인 메커니즘을 필요로 합니다. 데이터 웨어하우스를 구축하거나, 마이크로서비스 통신을 활성화하거나, 읽기 복제본을 통한 고가용성을 보장하는 경우에도 분산 시스템 간의 데이터를 동기화하는 문제는 어디에나 존재합니다. 일괄 작업이나 사용자 지정 애플리케이션 수준 CDC와 같은 전통적인 방법은 종종 복잡성, 지연 시간 및 운영 오버헤드를 발생시킵니다. 바로 여기서 PostgreSQL의 논리적 복제가 강력하고 우아한 솔루션으로 등장합니다. 변경 사항을 캡처하고 적용할 수 있는 기본적이고 강력하며 매우 구성 가능한 방법을 제공하여 분산 데이터 관리를 위한 아키텍처를 크게 단순화합니다. 이 문서는 PostgreSQL 논리적 복제의 복잡성을 탐구하고, 데이터베이스 간의 원활한 데이터 동기화 및 효율적인 변경 데이터 캡처(CDC)를 위해 이를 활용하는 방법을 시연합니다.
논리적 복제 이해하기
PostgreSQL 논리적 복제의 기능을 완전히 이해하려면 먼저 몇 가지 핵심 개념을 이해하는 것이 중요합니다.
- 논리적 복제: 데이터 개체와 해당 변경 사항을 물리적 저장 블록이 아닌 논리적 표현(INSERT, UPDATE, DELETE 문)을 기반으로 복제하는 방법입니다. 이를 통해 서로 다른 PostgreSQL 주 버전 간에 복제하거나 사용자 지정 디코더를 사용하여 PostgreSQL이 아닌 시스템으로도 복제할 수 있습니다.
- 물리적 복제: 블록 수준에서 운영 체제 파일을 포함한 전체 데이터베이스 클러스터를 복제하는 전통적인 방법입니다. 주로 재해 복구 및 복제본이 기본 복사본인 읽기 전용 복제본 시나리오에 사용됩니다.
- 쓰기 전 로그 (WAL): 데이터 무결성 및 내구성을 보장하는 PostgreSQL의 핵심 메커니즘입니다. 데이터베이스에 대한 모든 변경은 실제 데이터 파일에 적용되기 전에 먼저 WAL에 기록됩니다. 논리적 복제는 WAL에서 직접 데이터 변경 사항을 추출합니다.
- 게시: 복제를 위해 표시된 게시자 데이터베이스의 테이블 집합(선택적으로 스키마의 모든 테이블 또는 데이터베이스의 모든 테이블)입니다. 게시자는 보낼 데이터를 정의합니다.
- 구독: 구독자 데이터베이스에서 게시물에 대한 연결입니다. 구독자는 게시물에서 변경 사항을 수신하고 적용합니다.
- 논리 디코딩: 이진 WAL 형식을 복제를 위해 쉽게 이해하고 처리할 수 있는 논리적 형식(예: SQL 문, JSON, Avro)으로 변환하는 프로세스입니다.
논리적 복제의 메커니즘
PostgreSQL 논리적 복제는 게시자의 WAL에서 변경 사항을 캡처하여 구독자에게 전송하는 방식으로 작동합니다. 이 프로세스는 여러 단계로 나눌 수 있습니다.
- WAL 생성: 게시자 데이터베이스의 모든 DML 작업(INSERT, UPDATE, DELETE)은 WAL에 해당 항목을 생성합니다.
- 논리 디코딩: 논리 디코딩 플러그인(내장 논리적 복제의 기본값인
pgoutput
과 같은)은 이러한 WAL 항목을 읽고 변경 사항의 논리적 스트림으로 변환합니다. - 게시: 게시자는 복제하려는 테이블 또는 스키마를 지정하는 게시물을 생성합니다. 이는 논리적 변경 스트림의 필터 역할을 합니다.
- 구독: 구독자는 게시자의 특정 게시물을 가리키는 구독을 생성합니다.
- 데이터 전송: 구독자는 게시자에 연결합니다. 그런 다음 게시자는 구독된 테이블의 초기 데이터 스냅샷을 구독자에게 보냅니다. 초기 동기화 후에는 논리적 변경 사항(INSERT, UPDATE, DELETE)을 구독자에게 지속적으로 스트리밍합니다.
- 적용: 구독자는 이러한 논리적 변경 사항을 수신하여 로컬 테이블에 적용하여 데이터를 효과적으로 동기화합니다.
구현 및 예제
두 PostgreSQL 인스턴스 간에 논리적 복제를 설정하는 실제 예제를 살펴보겠습니다.
전제 조건:
두 개의 PostgreSQL 인스턴스(예: pg1
및 pg2
)가 필요합니다. 네트워크를 통해 통신할 수 있는지 확인하십시오. 이 예에서는 pg1
이 게시자이고 pg2
가 구독자라고 가정합니다.
1단계: 게시자 (pg1
)에서 PostgreSQL 구성
pg1
의 postgresql.conf
를 편집하고 다음 매개변수를 설정합니다.
# /path/to/pg1/data/postgresql.conf wal_level = logical max_replication_slots = 10 # 필요에 따라 조정 max_wal_senders = 10 # 필요에 따라 조정
변경 사항을 적용하려면 pg1
인스턴스를 다시 시작하십시오.
다음으로, 구독자로부터 연결을 허용하기 위해 pg1
의 pg_hba.conf
를 엽니다. 다음과 유사한 줄을 추가합니다(실제 IP 주소 또는 범위로 subscriber_ip
를 바꾸십시오).
# /path/to/pg1/data/pg_hba.conf
host replication all subscriber_ip/32 md5
pg_hba.conf
를 수정한 경우 pg1
을 다시 시작하십시오.
2단계: 게시자 (pg1
)에서 게시물 생성
슈퍼유저 또는 REPLICATION
권한이 있는 사용자로 pg1
에 연결합니다.
-- pg1에 연결 psql -h localhost -p 5432 -U postgres -- 시연을 위해 데이터베이스 및 테이블 만들기 CREATE DATABASE publisher_db; \c publisher_db; CREATE TABLE products ( product_id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, price DECIMAL(10, 2) NOT NULL, last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 'products' 테이블에 대한 게시물 생성 CREATE PUBLICATION my_publication FOR TABLE products; -- 선택적으로, 현재 데이터베이스의 모든 테이블을 복제하려면: -- CREATE PUBLICATION my_all_tables_publication FOR ALL TABLES;
3단계: 구독자 데이터베이스 (pg2
) 준비
슈퍼유저로 pg2
에 연결합니다.
-- pg2에 연결 psql -h localhost -p 5433 -U postgres -- 게시자와 동일한 스키마를 가진 수신자 테이블 만들기 -- 구독자 테이블 스키마가 호환되는 것(최소한 동일한 기본 키 및 열 이름/유형)이 중요합니다. CREATE DATABASE subscriber_db; \c subscriber_db; CREATE TABLE products ( product_id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, price DECIMAL(10, 2) NOT NULL, last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
4단계: 구독자 (pg2
)에서 구독 생성
pg2
의 subscriber_db
에서 구독을 생성합니다.
-- pg2의 subscriber_db에 연결 psql -h localhost -p 5433 -U postgres -d subscriber_db CREATE SUBSCRIPTION my_subscription CONNECTION 'host=localhost port=5432 user=postgres dbname=publisher_db password=your_pg1_password' PUBLICATION my_publication;
pg1
인스턴스의 실제 호스트 및 포트로 localhost
및 5432
를 바꾸고 pg1
의 postgres
사용자에 대한 암호로 your_pg1_password
를 바꾸십시오.
5단계: 복제 테스트
이제 게시자에 일부 데이터를 삽입하고 구독자에서 관찰해 보겠습니다.
pg1
에서 (publisher_db
내):
INSERT INTO products (name, price) VALUES ('Laptop', 1200.00); INSERT INTO products (name, price) VALUES ('Mouse', 25.50); UPDATE products SET price = 1150.00 WHERE name = 'Laptop'; DELETE FROM products WHERE name = 'Mouse';
pg2
에서 (subscriber_db
내):
SELECT * FROM products;
업데이트된 가격이 포함된 Laptop
항목이 표시되고 Mouse
항목은 없어져 성공적인 복제를 입증해야 합니다.
응용 시나리오
논리적 복제는 매우 다재다능하며 수많은 시나리오에서 적용될 수 있습니다.
- 변경 데이터 캡처 (CDC): 논리적 복제 스트림을 모니터링함으로써 외부 애플리케이션은 데이터베이스 변경에 실시간으로 반응할 수 있습니다. 이는 이벤트 기반 아키텍처, 감사 또는 스트림 처리에 기본적입니다.
- 데이터베이스 간 데이터 동기화: 마이크로서비스, 데이터 마트 또는 지리적으로 분산된 애플리케이션에 대해 다른 데이터베이스 간의 일관된 데이터를 유지합니다.
- 데이터 웨어하우싱 및 ETL: 데이터 웨어하우스에 변경 사항을 점진적으로 공급하여 대량 데이터 로드의 필요성을 줄입니다.
- 제로 다운타임 업그레이드: 이전 PostgreSQL 버전에서 최신 버전으로 데이터를 복제하여 메이저 버전 업그레이드 중 다운타임을 최소화합니다.
- 다중 마스터 (활성-활성) 복제 (주의 필요): 일반적으로 애플리케이션 계층에서 추가적인 충돌 해결 로직이 필요하며 복잡하지만, 논리적 복제는 사용자 지정 다중 마스터 설정을 위한 백본을 형성할 수 있습니다.
- 선택적 복제: 특정 테이블 또는 스키마만 복제하여 동기화되는 데이터에 대한 세분화된 제어를 허용하며, 이는 물리적 복제로는 불가능합니다.
- 외부 시스템과의 데이터 공유: 사용자 지정 논리 디코딩 플러그인을 사용하면 데이터를 PostgreSQL이 아닌 데이터베이스 또는 메시지 큐로 복제할 수 있으며, 이는 범용 데이터 변경 스트림 역할을 합니다.
결론
PostgreSQL 논리적 복제는 데이터베이스 간 데이터 동기화 및 변경 데이터 캡처와 같은 중요한 데이터베이스 작업에 대해 강력하고 효율적이며 기본 솔루션을 제공합니다. WAL 및 게시/구독 모델을 활용하여 데이터 흐름에 대한 세분화된 제어를 제공하여 매우 탄력적이고 분산된 데이터 아키텍처를 구축할 수 있습니다. 다재다능함 덕분에 최신 애플리케이션 개발 및 데이터 관리에 필수적인 도구가 되어 개발자가 전체 에코시스템에 걸쳐 일관된 데이터를 가진 확장 가능한 시스템을 구축할 수 있도록 지원합니다.