PostgreSQL 연결 프록시 선택하기
James Reed
Infrastructure Engineer · Leapcell

소개: 데이터베이스 성능의 숨은 영웅
고성능 애플리케이션의 세계에서 데이터베이스 액세스는 종종 중요한 병목 현상입니다. PostgreSQL 데이터베이스에 대한 모든 새 연결은 인증, 프로세스 생성 및 리소스 할당과 같은 오버헤드를 발생시킵니다. 빈번하고 단명하는 연결이 많은 애플리케이션의 경우 이러한 오버헤드가 성능을 빠르게 저하시키고 서버 리소스를 고갈시킬 수 있습니다. 플래시 세일 중인 인기 있는 전자 상거래 사이트를 상상해 보세요. 수천 명의 사용자가 동시에 연결을 시도합니다. 이러한 연결을 효율적으로 관리하는 메커니즘 없이는 데이터베이스가 따라잡기 어려울 것입니다. 여기서 연결 풀링 프록시가 등장합니다. 애플리케이션과 PostgreSQL 서버 사이에 위치하여 모든 요청에 대해 새 연결을 설정하는 대신 기존 연결을 지능적으로 재사용합니다. 이러한 프록시는 조용하고 효율적인 게이트 키퍼 역할을 하여 확장성과 응답성을 크게 향상시킵니다. PostgreSQL 생태계에서 이 중요한 역할을 위해 두 가지 저명한 오픈 소스 솔루션이 두드러집니다: PgBouncer와 Pgpool-II. 이들의 미묘한 차이를 이해하는 것이 데이터베이스 인프라를 최적화하는 데 핵심이며, 이 기사에서는 각각의 강점과 이상적인 애플리케이션을 안내할 것입니다.
PgBouncer vs. Pgpool-II: 연결 관리 파헤치기
PgBouncer와 Pgpool-II의 구체적인 내용을 살펴보기 전에 작동의 기초가 되는 핵심 개념에 대한 기본적인 이해를 갖추도록 하겠습니다.
핵심 용어
- 연결 풀링(Connection Pooling): 각 요청에 대해 새 연결을 여는 대신 기존 데이터베이스 연결을 재사용하는 관행입니다. 이는 연결 설정 및 해제와 관련된 오버헤드를 줄여 성능 및 리소스 활용도를 향상시킵니다.
- 프록시(Proxy): 다른 서버에서 리소스를 찾는 클라이언트의 요청에 대한 중개자 역할을 하는 서버입니다. 이 맥락에서는 애플리케이션과 PostgreSQL 데이터베이스 사이에 위치합니다.
- 로드 밸런싱(Load Balancing): 단일 서버가 병목 현상이 되지 않도록 여러 백엔드 서버에 들어오는 네트워크 트래픽을 분산하는 것입니다. 이는 애플리케이션 응답성 및 가용성을 향상시킵니다.
- 고가용성(High Availability, HA): 종종 단일 실패 지점을 제거하고 장애 조치 메커니즘을 제공하여 높은 수준의 운영 연속성을 보장하는 시스템 설계 접근 방식입니다.
- 읽기 복제본(Read Replicas): 읽기 전용 쿼리를 서비스할 수 있는 마스터 데이터베이스의 복사본으로, 기본 데이터베이스의 부하를 덜어주고 읽기 확장성을 향상시킵니다.
- 문장 수준 풀링(Statement-level Pooling): 각 SQL 문이 실행된 후 물리적 연결이 풀로 반환되는 풀링 모드입니다. 이는 연결 재사용의 가장 높은 잠재력을 제공하지만 주의 깊게 처리하지 않으면 여러 문장에 걸친 트랜잭션을 손상시킬 수 있습니다.
- 트랜잭션 수준 풀링(Transaction-level Pooling):
COMMIT또는ROLLBACK이 완료된 후 물리적 연결이 풀로 반환되는 풀링 모드입니다. 이는 트랜잭션 경계에 의존하는 애플리케이션에 더 안전합니다. - 세션 수준 풀링(Session-level Pooling): 물리적 연결이 클라이언트가 연결을 끊을 때까지 클라이언트에 전용되는 풀링 모드입니다. 이는 직접 연결과 가장 유사하게 작동하지만 짧은 클라이언트 세션에 대한 풀링 이점을 제공합니다.
PgBouncer: 가볍고 강력한 연결 머신
PgBouncer는 단순성, 효율성 및 연결 풀링에 대한 집중으로 유명합니다. 이것은 한 가지 작업을 매우 잘 수행하도록 설계된 가볍고 단일 프로세스, 다중 스레드 프록시입니다: 연결 관리.
원칙 및 기능
PgBouncer는 백엔드 PostgreSQL에 대한 일련의 연결을 열고 들어오는 클라이언트 연결에 이 연결을 할당하여 작동합니다. 클라이언트가 연결을 끊으면 백엔드 연결이 풀로 반환되어 다음 클라이언트를 준비합니다. 주요 강점은 다양한 풀링 모드에 있습니다:
- 세션 풀링(기본값): 서버 연결은 전체 세션 기간 동안 클라이언트 세션에 할당됩니다. 클라이언트가 연결을 끊으면 서버 연결이 풀로 반환됩니다. 세션 상태를 완벽하게 유지하므로 가장 안전한 모드입니다.
- 트랜잭션 풀링: 서버 연결은 트랜잭션 기간 동안에만 클라이언트에 할당됩니다.
COMMIT또는ROLLBACK후 연결이 풀로 반환됩니다. 이를 통해 더 많은 연결 재사용이 가능하지만 애플리케이션이 트랜잭션 경계를 인식해야 합니다.SET TIMEZONE과 같은 모든 세션별 상태는 트랜잭션 간에 재설정됩니다. - 문장 풀링: 서버 연결은 단일 문장 기간 동안에만 클라이언트에 할당됩니다. 문장이 완료되면 연결이 반환됩니다. 이를 통해 가장 높은 재사용이 가능하지만 다중 문장 트랜잭션을 손상시키고 애플리케이션에서 신중하게 처리하지 않으면 예기치 않은 동작으로 이어질 수 있으므로 가장 위험합니다.
구현 및 구성 예제
PgBouncer는 일반적으로 pgbouncer.ini인 간단한 INI 스타일 파일을 통해 구성됩니다.
; pgbouncer.ini 예제 [databases] ; 데이터베이스를 정의합니다. 클라이언트 대면 데이터베이스 이름을 ; 잠재적으로 다른 백엔드 데이터베이스 이름 및 자격 증명에 매핑할 수 있습니다. mydb = host=localhost port=5432 dbname=my_appdb user=webapp_user password=mypwd pool_size=20 [pgbouncer] ; 포트 6432의 모든 인터페이스에서 수신 listen_addr = * listen_port = 6432 ; 인증 파일 auth_type = md5 auth_file = users.txt ; 연결 풀링 모드 pool_mode = transaction ; 허용되는 최대 클라이언트 연결 수 max_client_conn = 1000 ; 데이터베이스에 대한 기본 최대 서버 연결 수 default_pool_size = 10 ; 풀에서 유지할 최소 서버 연결 수 min_pool_size = 5 ; 연결을 종료하기 전에 릴리스될 때까지 기다리는 시간 server_idle_timeout = 60 ; 연결을 사용할 수 있을 때까지 기다리는 시간 query_wait_timeout = 120 ; 로그 수준 (0=DEBUG, 1=INFO, 2=NOTICE, 3=WARNING, 4=ERROR, 5=FATAL) logfile = /var/log/pgbouncer.log pidfile = /var/run/pgbouncer/pgbouncer.pid
users.txt 파일에는 인증을 위한 username "password_hash" 항목이 포함됩니다.
PgBouncer를 실행하려면:
pgbouncer -d /path/to/pgbouncer.ini
애플리케이션은 PostgreSQL에 직접 연결하는 대신 PgBouncer의 listen_port(예: 6432)에 연결합니다.
애플리케이션 시나리오
- 고성능 웹 애플리케이션: 웹 서버 또는 마이크로서비스와 같이 짧은 요청에 대해 데이터베이스 연결을 자주 열고 닫는 애플리케이션에 적합합니다.
- 리소스 제약 환경: 최소 오버헤드로 인해 PgBouncer는 모든 리소스가 중요한 컨테이너 또는 소규모 VM에 적합합니다.
- 단순 연결 풀링: 고급 기능(로드 밸런싱 또는 고가용성) 없이 효율적인 연결 재사용이 주요 요구 사항인 경우.
Pgpool-II: 기능이 풍부한 스위스 아미 나이프
Pgpool-II는 연결 풀링뿐만 아니라 로드 밸런싱, 복제 및 고가용성 기능을 제공하는 보다 포괄적인 솔루션입니다. PostgreSQL을 위한 "미들웨어"로 설계되어 확장성 및 안정성을 향상시키는 광범위한 기능을 제공합니다.
원칙 및 기능
Pgpool-II는 백엔드 PostgreSQL 서버로 쿼리를 라우팅하여 읽기 쿼리를 복제본에 지능적으로 분산하고 쓰기 쿼리를 기본 서버로 보냅니다. 주요 기능은 다음과 같습니다:
- 연결 풀링: PgBouncer와 유사하게 백엔드 서버에 대한 연결을 재사용합니다.
- 로드 밸런싱(읽기/쓰기 분할):
SELECT문을 읽기 복제본으로 자동 라우팅하여 기본 서버의 부하를 줄이고,INSERT,UPDATE,DELETE문(및 기타 DDL/DML)을 기본 서버로 보냅니다. - 고가용성 및 자동 장애 조치: 백엔드 PostgreSQL 서버를 모니터링하고 기본 서버 장애 발생 시 자동으로 대기 서버로 전환하여 지속적인 운영을 보장할 수 있습니다.
- 복제 관리: 다양한 PostgreSQL 복제 설정(예: 스트리밍 복제, 논리 복제)을 관리할 수 있습니다.
- 쿼리 캐싱: 쿼리 결과를 캐시하여 읽기 집약적인 워크로드를 더욱 빠르게 처리합니다(이 기능은 종종 애플리케이션 수준 캐시 또는 더 전문화된 도구에서 처리됩니다).
- 인밴드 관리: 관리 명령을 위한 의사 데이터베이스를 제공합니다.
구현 및 구성 예제
Pgpool-II의 구성은 더 풍부한 기능 세트로 인해 더 복잡하며 일반적으로 pgpool.conf를 통해 관리됩니다.
; pgpool.conf 예제 ; 연결 풀링 설정 num_init_children = 32 ; 시작할 Pgpool-II 자식 프로세스 수 max_pool = 4 ; 백엔드에 대한 각 Pgpool-II 자식당 최대 연결 수 connection_cache = on ; 연결 캐싱 활성화 ; 백엔드 서버 정의 backend_hostname0 = '192.168.1.10' backend_port0 = 5432 backend_weight0 = 1 backend_data_directory0 = '/var/lib/postgresql/13/main' backend_flag0 = 'ALLOW_FAILBACK' ; 기본값의 경우 backend_hostname1 = '192.168.1.11' backend_port1 = 5432 backend_weight1 = 1 backend_data_directory1 = '/var/lib/postgresql/13/main' backend_flag1 = 'ALLOW_FAILBACK' ; 대기/복제본의 경우 ; 로드 밸런싱 설정 load_balance_mode = on ; 읽기/쓰기 분할 활성화 replication_mode = on ; 복제된 백엔드 처리 시 ; 고가용성 설정 health_check_period = 5 ; 백엔드 상태 확인 주기 failover_command = '/etc/pgpool2/failover.sh %d %H %W %P %r' ; 장애 조치 시 실행할 스크립트 failback_command = '/etc/pgpool2/failback.sh %d %H %W %P %r' ; 실패 복구 시 실행할 스크립트 ; 인증 설정 listen_addresses = '*' port = 9999 auth_methods = 'md5' pg_md5 = '/etc/pgpool2/pg_md5' ; Pgpool-II 자체 사용자 인증 파일 ; 백엔드로 인증 위임 enable_pool_hba = on pool_hba_file = '/etc/pgpool2/pool_hba.conf' ; PostgreSQL의 pg_hba.conf와 유사
pg_md5에는 Pgpool-II 관리용 username:password_hash가 포함됩니다. pool_hba.conf는 Pgpool-II에 대한 클라이언트 액세스를 제어합니다.
Pgpool-II를 시작하려면:
pgpool -n -d
애플리케이션은 Pgpool-II의 port(예: 9999)에 연결합니다.
애플리케이션 시나리오
- 읽기 집약적인 워크로드를 위한 확장성: 읽기/쓰기 분할 기능은
SELECT쿼리 비율이 높은 애플리케이션에 매우 중요하며 여러 복제본에 효율적으로 분산됩니다. - 자동 고가용성: 기본 데이터베이스 실패 시 자동 장애 조치 기능과 함께 최소한의 다운타임을 보장해야 하는 미션 크리티컬 시스템에 적합합니다.
- 단순화된 데이터베이스 아키텍처: 애플리케이션이 여러 PostgreSQL 서버에 대한 단일 진입점을 갖도록 하고 싶을 때 복제 및 장애 조치의 복잡성을 애플리케이션 계층에서 추상화합니다.
- 레거시 애플리케이션: 기본적으로 읽기 복제본 또는 장애 조치를 지원하지 않는 이전 애플리케이션에 유용하여 이러한 기능을 외부에서 추가할 수 있습니다.
프록시 선택
PgBouncer와 Pgpool-II 간의 결정은 주로 특정 요구 사항 및 아키텍처 목표에 따라 달라집니다:
| 기능/고려 사항 | PgBouncer | Pgpool-II |
|---|---|---|
| 주요 목표 | 효율적인 연결 풀링 | 로드 밸런싱, HA, 복제, 연결 풀링 |
| 복잡성 | 단순, 가볍고 구성 용이 | 더 복잡하고 기능이 풍부하며 학습 곡선이 가파름 |
| 리소스 사용량 | 최소 | 더 높음 (더 많은 기능으로 인해) |
| 성능(풀링) | 뛰어남, 빠른 연결 재사용에 최적화됨 | 좋지만 추가 기능으로 인한 오버헤드 존재 |
| 로드 밸런싱 | 네이티브 로드 밸런싱 없음 | 예, 지능적인 읽기/쓰기 분할 |
| 고가용성 | 네이티브 HA/장애 조치 없음 | 예, 자동 장애 조치 및 상태 확인 |
| 읽기 복제본 | 단일 복제본에 대한 연결 풀링 가능하지만 여러 복제본에 걸친 지능적인 라우팅 또는 로드 밸런싱은 없음. | 예, 복제본 간에 읽기 분산 |
| 복제 관리 | 아니요 | 예, 다양한 복제 설정 관리 |
| 쿼리 캐싱 | 아니요 | 예 (결과 캐시 가능) |
| 최적 사용 사례 | 단일 데이터베이스 인스턴스(기본 또는 복제본)에 대한 순수 연결 멀티플렉싱이 필요한 애플리케이션. | 애플리케이션 계층에서 복제 세부 정보를 추상화하여 로드 밸런싱, HA 및 복제가 필요한 애플리케이션. |
PgBouncer를 사용하는 경우:
- 주요 관심사가 단순히 연결 오버헤드를 줄이는 것입니다.
- 애플리케이션에서 이미 읽기/쓰기 분할을 처리하거나 기본 데이터베이스에만 연결하는 경우.
- 최소한의 오버헤드를 가진 미니멀리스트 솔루션을 찾는 경우.
- 다른 계층(예: 클라우드 제공업체 서비스, Patroni)에서 HA를 관리하는 경우.
Pgpool-II를 사용하는 경우:
- 확장을 위해 여러 복제본에 걸쳐 읽기 쿼리를 분산해야 하는 경우.
- 고가용성을 보장하기 위해 자동 장애 조치 기능을 원하는 경우.
- 애플리케이션의 데이터베이스 상호 작용을 단순화하고 복제 세부 정보를 추상화하고 싶은 경우.
- PostgreSQL 서버 클러스터에 대한 애플리케이션의 단일 진입점이 필요한 경우.
또한 일부 고급 설정에서는 PgBouncer를 Pgpool-II 위에 계층화하거나 Patroni와 같은 다른 HA 솔루션과 함께 사용하는 것을 볼 수도 있습니다. 예를 들어, PgBouncer는 Pgpool-II의 HA 기능 없이 연결 풀링을 제공하는 Patroni 클러스터 앞에 위치할 수 있습니다.
결론: PostgreSQL 프런트엔드 맞춤화
PgBouncer와 Pgpool-II 간의 선택은 애플리케이션의 확장성, 가용성 및 복잡성에 대한 특정 요구 사항의 신중한 평가로 귀결됩니다. PgBouncer는 최소한의 공간으로 우아하고 고성능의 연결 풀링을 제공하여 순수 멀티플렉싱에 이상적입니다. 반면에 Pgpool-II는 애플리케이션에서 상당한 복잡성을 추상화하면서 고급 로드 밸런싱, 고가용성 및 복제 관리를 위한 강력하고 기능이 풍부한 미들웨어를 제공합니다. 둘 다 강력한 도구이지만 올바른 선택은 PostgreSQL 인프라가 최적으로 성능을 발휘할 뿐만 아니라 미래의 요구 사항을 충족하도록 우아하게 확장되도록 보장합니다.

