※ 이 글은 PostgreSQL의 built-in replication 방식을 학습하기 위해 일부 공식 문서와 관련 개념을 정리한 글입니다.
0. 서론
본론에 앞서 중요한 개념인 고가용성과 로드 밸런싱에 대해 알아보자.
고가용성(High Availability, HA)을 쉽게 말하자면 한 장비 또는 여러 장비가 다운될 때도 시스템이 계속 동작하게 하는 것이다. 고가용성을 달성하기 위해서 서버를 이중화(Redundancy)해서 주 서버에 장애가 발생했을 때 예비 서버로 빠르게 전환될 수 있도록 해야 한다.
서버, 네트워크, 프로그램 등의 정보 시스템이 상당히 오랜 기간 동안 지속적으로 정상 운영이 가능한 성질을 말한다. 고(高)가용성이란 "
가용성이 높다"는 뜻으로서, "절대 고장 나지 않음"을 의미한다. 고가용성은 흔히 가용한 시간의 비율을 99%, 99.9% 등과 같은 퍼센티지로 표현하는데, 1년에 계획된 것 제외 5분 15초 이하의 장애시간을 허용한다는 의미의 파이브 나인스(5 nines), 즉 99.999%는 매우 높은 수준으로 고품질의 데이터센터에서 목표로 한다고 알려져 있다.
출처 : https://ko.wikipedia.org/wiki/고가용성
로드 밸런싱(Load Balancing)은 통신 흐름을 제어해서 사용할 서버를 분산하는 기술로 데이터베이스 부하 분산 외에도 DNS 라운드 로빈, 콘텐츠 전송 등에 사용된다.
부하분산 또는 로드 밸런싱(load balancing)은 컴퓨터 네트워크 기술의 일종으로 둘 혹은 셋 이상의 중앙처리장치 혹은 저장장치와 같은 컴퓨터 자원들에게 작업을 나누는 것을 의미한다. 이로써 가용성 및 응답시간을 최적화 시킬 수 있다.
출처 : https://ko.wikipedia.org/wiki/부하분산
데이터베이스 서버를 안정적이고 빠른 시스템으로 구축하기 위해서는 위의 두 개념이 중요하다. 우리가 기대하는 데이터베이스 서버로의 역할이 문제없이 수행되기 위해서는 여러 데이터베이스 서버 간의 원활한 작업이 필요하기 때문이다. 주 서버에 장애가 발생했다면 빠르게 예비 서버들이 작업을 이어받아 처리할 수 있어야 하고, 여러 서버가 같은 데이터를 제공할 수 있도록 해야 한다.
예를 들어, 정적 웹 페이지를 제공하는 웹 서버는 여러 서버에 요청을 분산하여 해당 작업이 원활히 수행되게 한다. 읽기 전용 데이터베이스 서버도 부하가 생겼다면 분산 처리를 하면 된다. 하지만 읽기와 쓰기 작업 요청이 한데 섞일 때 문제가 발생한다. 읽기 전용 데이터는 각 서버에 한 번씩만 적재되면 되지만 쓰기 작업의 경우 모든 서버에 변경 사항이 전달되어야 하며 이 과정 중에 동기화 문제가 발생할 수 있기 때문이다. 그래서 데이터의 일관성을 유지하는 것이 중요하다. 쓰기 작업이 일어난 이후에 모든 서버에서 동일한 데이터를 읽을 때 일관된 결과를 반환해야 하는 것이다.
이런 동기화 문제를 해결하는 방법들을 알아보도록 하자.
1️⃣ Primary Server & Sencondary Server
한 서버만 데이터 수정을 할 수 있도록 설정하고 그 서버의 변화를 추적하는 서버를 두는 방법이다. 이때 역할에 따라 서버의 명칭이 달라지는데 데이터를 수정할 수 있는 서버는 Read/Write 서버, Master 서버 또는 주(Primary) 서버라고 하며 이 주 서버에서의 변경 사항을 추적하는 서버는 Standby 서버 또는 보조(Secondary) 서버라고 부른다. 아래 그림처럼 Standby 서버는 두 개의 종류로 나뉘어진다.
- Warm Standby Server : 주 서버가 되기 전까지 연결할 수 없는 서버.
- Hot Standby Server : 사용자가 연결할 수 있고 읽기 전용 쿼리를 처리할 수 있는 서버
2️⃣ Synchronous & Asynchronous
상황에 따라 동기식과 비동기식을 적용해 문제를 해결할 수도 있다. 동기식(Synchronous)을 사용하면 데이터를 수정하는 트랜잭션이 모든 데이터베이스 서버에서 커밋될 때까지 완료되지 않는다. 해당 방법의 장점은 다음과 같다.
- 장애 조치(Failover) 시 데이터 손실이 발생하지 않는다.
- 어떤 서버를 조회하더라도 일관된 결과를 반환하도록 보장한다.
반대로 비동기식(Asynchronous)은 동기식 방식이 너무 느릴 경우에 사용한다. 데이터가 수정된 이후에 변경 사항이 다른 서버로 전파되기까지 지연될 수 있어서 서버가 전환될 때 일부 트랜잭션이 손실되거나 로드 밸런싱된 서버가 아직 업데이트되지 않은 결과를 반환할 가능성이 있다.
3️⃣ Granularity
전체 데이터베이스 서버 또는 테이블, 데이터베이스 수준으로 제어할 수 있는 단위를 세분화(Granularity)하여 해결할 수 있다. 특정 테이블에서 수정 작업이 빈번하게 일어난다면 해당 테이블만 업데이트되도록 설정할 수 있는 것이다.
위의 방식들 중 어떤 것을 선택하느냐는 프로젝트의 상황에 따라 성능과 기능성을 고려하면 된다. 예를 들어, 네트워크 속도가 느릴 경우에 동기식 방법을 사용하면 성능이 감소될 수 있으니 비동기식을 선택하는 것처럼 말이다.
1. 다양한 복제 방법 비교
아래의 표를 보면 다양한 복제 방법들이 있다. PostgreSQL은 오픈 소스이기 때문에 이외에도 공개되지 않은 솔루션들이 있을 수 있다는 점을 참고하자.
공식 문서에서 다양한 방법들에 대한 간단한 소개들을 확인할 수 있으며 이 글에서는 PostgreSQL의 내장된 복제 방식들이 사용하는 기능에 대해 알아보려고 한다.
1️⃣ Write-Ahead Log Shipping
주 서버가 데이터 변경 사항을 WAL(Write-Ahead Log) 파일로 Standby 서버에게 전송할 수 있는 기능이다. 이 기능을 사용하면 Standby 서버는 WAL 레코드를 읽어서 데이터를 일관성있게 유지할 수 있다. WAL은 말그대로 데이터베이스에 변경 사항이 적용되기 전에 먼저 저장되는 로그를 뜻한다. WAL을 사용했을 때의 장점은 다음과 같다.
- 충돌이 발생했을 때 WAL을 사용하여 데이터베이스를 복구할 수 있다.
- 트랜잭션에 의해 변경된 모든 데이터 파일을 기록하지 않아도 되며 커밋 완료 사항에 대한 로그 파일만 디스크에 기록하면 되므로 디스크 쓰기 횟수가 크게 줄어든다.
- 로그 파일에 데이터가 순차적으로 기록되기 때문에 데이터 파일 전체를 기록하는 것보다 시간과 자원을 아낄 수 있다.
즉, 주 서버에 장애가 발생해도 Standby 서버에 업데이트된 데이터들을 가지고 있기 때문에 문제 없이 주 서버로 전환할 수 있게 된다. 또한 상황에 따라 동기식과 비동기식을 선택할 수 있고 전체 데이터베이스 서버 단위로 적용된다. 구현 방법은 두 가지로 파일 기반 로그 전송(File-Based Log Shipping)과 스트리밍 복제(Streaming Replication)이다. 필요에 따라 이 두 가지를 조합해서 사용할 수도 있다.
2️⃣ Logical Replication
주 서버가 데이터 변경 사항을 스트림(Stream) 형태로 Standby 서버에게 전송할 수 있는 기능이다. Log Shipping과는 다르게 테이블 단위로 적용할 수 있기 때문에 데이터베이스 전체를 복제할 필요 없이 특정 테이블이나 데이터의 정보만 유연하게 처리할 수 있다.
PosgreSQL의 논리적 복제는 WAL을 사용하는데 이 로그에서 데이터 변경 사항을 추출해서 스트림을 생성한다. 또한 데이터가 한 방향이 아니라 여러 방향으로 흐를 수 있어서 여러 서버들이 데이터를 교환하고 복제할 수있다는 장점이 있다.
2. PostgreSQl의 built-in replication
📦🚢 File-Based Log-Shipping
파일 기반 로그 전송(File-Based Log Shipping)은 WAL 기록을 파일로 보내 복제하는 방식이다. 주 서버에서 일어난 데이터 변경 사항이 16MB까지 도달하면 해당 파일이 Standby 서버로 전송되고 이때 파일을 기반으로 데이터를 복제한다. 공식 문서에 따르면 WAL 파일은 각 시스템의 지역 혹은 위치에 상관없이 쉽게 전송 가능하며 비용 또한 저렴하다.
이 과정 중에 지속적인 기록(Continuous Archiving)이 이뤄지기 때문에 Standby 서버는 주 서버에 장애가 발생했을 때 곧바로 주 서버로 전환해 서비스가 정상 운영되도록 할 수 있다. 주 서버는 지속적으로 기록하고 Standby 서버는 지속적으로 복구하는 형태라고 생각하면 된다. 백업된 데이터를 한꺼번에 복구하는 게 아니기 때문에 속도가 빠르며 고가용성이 보장된다. 이때 앞서 설명한 것과 같이 Standby 서버가 두 종류로 나뉘게 된다.
- Warm Standby (PostgreSQL 8.3 도입) : 사용자가 연결할 수 없으며 읽기 작업이 불가하다.
- Hot Standby (PostgreSQL 9.0 도입) : 사용자가 연결할 수 있으며 읽기 작업이 가능하다.
하지만 트랜잭션 커밋 후에 WAL가 전달되기 때문에 데이터 손실 가능성이 있다는 단점이 있다. archive_timeout이라는 설정 값으로 파일 전송 주기를 짧게 설정해 데이터 손실을 방지할 수 있지만 너무 짧게 설정하면 성능에 안 좋은 영향을 끼칠 수 있으니 신중히 선택해야 한다. 혹은 파일 기반 대신 데이터 손실 가능성이 낮은 스트리밍 복제(Streaming Replication)를 선택할 수도 있다.
📡🔄 Streaming Replication
스트리밍 복제(Streaming Replication)는 주 서버에서 WAL 파일이 완성되기 전에 변경 사항들을 실시간으로 스트리밍하는 방식이다. 실시간으로 전송되기 때문에 Standby 서버가 기존의 파일 기반 전송 방식보다 최신 데이터를 유지할 수 있게 된다. 기본적으로 비동기 방식으로 작동하기 때문에 데이터 변경 사항이 커밋된 후 Standby 서버로 전달되는 데 약간의 데이터 지연(Data Lag)이 발생한다. 하지만 1초 이내이기 때문에 파일 기반 방식보다 소요 시간이 적고 성능에 문제가 되지 않는다.
만약 스트리밍 복제를 파일 기반 로그 전송 방식에서 사용된 지속적인 기록(Countinuous Archiving)없이 사용할 경우에 주 서버에서 스트리밍 해야 할 WAL 세그먼트를 전송하기도 전에 새로운 변경 사항으로 덮어씌우는 세그먼트 재활용 문제가 발생할 수 있다. 만약 Standby 서버가 덮어씌워진 WAL 세그먼트를 전달받는다면 데이터 불일치가 생기게 되고 결국 해당 서버를 초기화해야 한다. 이 문제는 어떻게 방지할 수 있을까?
- 단기적 데이터 유실 방지
- wal_keep_size 설정 값을 충분히 크게 설정하여 WAL 세그먼트를 더 유지할 수 있게 한다.
- 복제 슬롯을 구성하여 WAL 세그먼트를 보존할 수 있게 한다.
- 장기적 데이터 유실 방지
- archive_mode를 on 상태로 변경하여 WAL 파일이 저장될 때마다 별도의 위치로 복사한다.
이외에도 주 서버의 postgresql.conf 파일과 pg_hba.conf 파일의 설정 값들을 변경해서 Standby 서버가 주서버의 복제 가상 데이터베이스(replication pseudo-database)에 연결할 수 있도록 해야 한다. 만약 system이 keepalive 소켓 옵션을 지원하는 경우 tcp_keepalives_*를 설정하면 주 서버가 끊어진 연결을 빠르게 감지할 수도 있다. 이 설정 값들이 올바르게 설정되었다면 Standby 서버는 아카이브에 있는 모든 WAL 파일로 복제 작업을 수행한 뒤에 주 서버에 연결되는 것을 확인할 수 있을 것이다.
🧩🔀 Logical Replication
논리적 복제(Logical Replication)은 데이터 객체와 그 변경 사항을 복제하는 방법으로 주로 기본 키(Primary Key)를 기준으로 복제한다. 주 서버는 Publisher 역할, Standby 서버는 Subscriber 역할을 하게 되며 주 서버에서 데이터의 변경 사항이 생기면 그 변경 사항을 게시(Publication)하고 Standby 서버는 그 게시된 변경 사항을 구독(Subscription)하는 형태로 데이터를 받게 된다.
초기에는 주 서버의 데이터베이스 데이터의 스냅샷(Snapshot)을 활용해 Standby 서버로 데이터를 복사한다. 이 과정이 완료되면 Publisher의 변경 사항이 실시간으로 Subscriber에게 전송되는데 단일 구독(Single Subscription) 내에서 이루어지기 때문에 트랜잭션 일관성이 보장된다.
이 복제 방식은 데이터를 세밀하게 제어할 수 있기 때문에 다음과 같은 상황들에서 사용할 수 있다.
- 여러 데이터베이스를 하나의 데이터베이스로 통합
- PostgreSQL의 서로 다른 버전 간 복제
- 서로 다른 플랫폼 간 PostgreSQL 인스턴스 복제 (e.g. Linux -> Window)
3. 정리
각 복제 방식의 장점과 단점을 정리해 보았다. File-Based Log Shipping과 Streaming Replication의 비교에서 서로 보완되는 점들을 확인할 수 있고, 세밀한 제어의 아쉬움은 Logical Replication에서 채울 수 있음을 알 수 있다.
File-Based Log Shipping | Streaming Replication | Logical Replication | |
장점 | ✓ 간단한 설정 |
✓ 동기화 속도 빠름 ✓ 적은 데이터 손실 가능성 ✓ 동기화 모드에서 데이터 일관성 보장 |
✓ 세밀한 데이터 제어 가능 ✓ 버전, 플랫폼 간 복제 가능 ✓ 한 시스템의 데이터를 여러 시스템으로 전송 가능 |
단점 | ✓ 데이터 손실 가능성 ✓ 실시간 전송에 비해 동기화 속도 저하 |
✓ 파일 전송에 비해 대역폭 증가, 복잡한 설정 |
✓ 복잡한 설정 ✓ 트랜잭션이 많을 때의 성능 저하 |
최근 회사에서 Replication 설정을 할 때 Streaming Replication 방식을 사용했었다. 그런데 공유해 주신 문서를 바탕으로 빠르게 작업하다 보니 어떤 설정 값이 필요한 명확한 이유나 복제 방식 선택에 대한 확신을 가지지 못한 채로 작업을 마치게 됐다. 그래서 공식 문서를 참고해 관련 내용을 정리했는데 선택이 잘못되지 않았음을 확인할 수 있는 시간이 되었다.
Streaming 방식에서 세그먼트 재활용을 방지하기 위해 복제 슬롯을 사용하거나 archive_mode를 on으로 혹은 wal_keep_size를 일정 크기로 설정하는 방법들을 소개했었는데 여러 구성 예제들을 보면 한 가지만 설정하거나 혹은 최대 두 가지 정도를 설정해서 사용하는 것 같다. 실제로 나의 경우 archive_mode와 복제 슬롯을 활용했는데 상황에 따라 데이터 유실 가능성이 적은 적절한 방법을 선택하면 되겠다.
참고
'DB' 카테고리의 다른 글
[jOOQ] ad-hoc 방식을 통한 One-to-One, One-to-Many 조회 (0) | 2025.02.02 |
---|---|
[PostgreSQL] Docker를 활용한 Streaming Replication 설정 가이드 (2) | 2024.10.31 |
[jOOQ] jOOQ는 처음이라 (0) | 2024.07.12 |
[MyBatis] selectKey로 다중 컬럼 값 가져오기 (0) | 2023.05.30 |
[MyBatis] BindingException 해결 (feat. IntelliJ Gradle 설정) (1) | 2023.05.29 |