본문 바로가기
Database2025년 6월 30일8분 읽기

Connection Pool 최적화 — HikariCP PgBouncer 설정 가이드

YS
김영삼
조회 354

Connection Pool이 필요한 이유

데이터베이스 연결 생성은 TCP 핸드셰이크, 인증, 세션 초기화 등 비용이 큰 작업입니다. PostgreSQL은 연결당 프로세스를 포크하므로 연결 수가 많아지면 메모리와 CPU 오버헤드가 급증합니다. Connection Pool은 미리 생성한 연결을 재사용하여 이 비용을 제거합니다.

풀 크기 공식

// 최적 풀 크기 계산 (HikariCP 권장)
// connections = (core_count * 2) + effective_spindle_count
// SSD 서버 (spindle=1): 8코어 -> (8 * 2) + 1 = 17

// 일반적인 가이드라인
// - 웹 서버: 10-20 connections
// - 배치 처리: 5-10 connections
// - 마이크로서비스: 5-10 per instance

// 과다 설정의 문제
// 100개 연결 = PostgreSQL에서 100개 프로세스
// 컨텍스트 스위칭 비용 -> 20개가 100개보다 빠를 수 있음

HikariCP 설정 (Java/Spring)

# application.yml - Spring Boot HikariCP 설정
spring:
  datasource:
    url: jdbc:postgresql://db:5432/myapp
    username: app_user
    password: secret
    hikari:
      # 풀 크기 설정
      maximum-pool-size: 15          # 최대 연결 수
      minimum-idle: 5                # 최소 유휴 연결
      # 시간 설정
      connection-timeout: 30000      # 연결 획득 대기 (30초)
      idle-timeout: 600000           # 유휴 연결 유지 (10분)
      max-lifetime: 1800000          # 연결 최대 수명 (30분)
      keepalive-time: 300000         # 연결 유효성 확인 (5분)
      # 검증
      validation-timeout: 5000
      connection-test-query: "SELECT 1"
      # 모니터링
      pool-name: "MyApp-Pool"
      register-mbeans: true
      # 누수 감지
      leak-detection-threshold: 60000  # 60초 이상 반환 안 하면 경고

HikariCP 모니터링

// Micrometer를 통한 메트릭 수집
// 자동으로 수집되는 메트릭:
// - hikaricp.connections.active     : 사용 중인 연결
// - hikaricp.connections.idle       : 유휴 연결
// - hikaricp.connections.pending    : 대기 중인 요청
// - hikaricp.connections.timeout    : 타임아웃 횟수
// - hikaricp.connections.acquire    : 연결 획득 시간
// - hikaricp.connections.creation   : 연결 생성 시간

// Prometheus + Grafana 대시보드에서 모니터링
// 경고 기준:
// - pending > 0 이 지속되면 풀 크기 부족
// - active == maximum-pool-size 이면 포화 상태
// - timeout 발생 시 즉시 알림

// 프로그래밍 방식 확인
HikariPoolMXBean poolProxy = ((HikariDataSource) dataSource).getHikariPoolMXBean();
log.info("Active: {}, Idle: {}, Waiting: {}",
    poolProxy.getActiveConnections(),
    poolProxy.getIdleConnections(),
    poolProxy.getThreadsAwaitingConnection());

PgBouncer 설정 (PostgreSQL 전용)

PgBouncer는 PostgreSQL 앞에서 동작하는 경량 연결 풀러입니다. 수천 개의 클라이언트 연결을 소수의 PostgreSQL 연결로 다중화하여, 마이크로서비스 환경에서 특히 효과적입니다.

# /etc/pgbouncer/pgbouncer.ini

[databases]
myapp = host=127.0.0.1 port=5432 dbname=myapp
        auth_user=pgbouncer pool_size=20

[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt

# 풀링 모드
pool_mode = transaction    # transaction | session | statement

# 풀 크기
default_pool_size = 20     # DB당 기본 풀 크기
min_pool_size = 5          # 최소 유지 연결
max_client_conn = 1000     # 최대 클라이언트 연결
max_db_connections = 50    # DB당 최대 연결

# 시간 설정
server_idle_timeout = 600  # 유휴 서버 연결 종료 (10분)
client_idle_timeout = 0    # 클라이언트 유휴 타임아웃 없음
server_lifetime = 3600     # 서버 연결 최대 수명 (1시간)
server_login_retry = 1     # 재연결 대기 (초)

# 로깅
log_connections = 1
log_disconnections = 1
log_pooler_errors = 1
stats_period = 60

풀링 모드 비교

모드연결 반환 시점장점제약사항
session클라이언트 연결 종료완전 호환풀링 효과 낮음
transaction트랜잭션 종료높은 다중화세션 변수, PREPARE 제한
statement쿼리 완료가장 높은 다중화멀티스테이트먼트 트랜잭션 불가

PgBouncer 관리 명령어

# PgBouncer 관리 콘솔 접속
psql -h 127.0.0.1 -p 6432 -U pgbouncer pgbouncer

-- 연결 풀 상태 확인
SHOW POOLS;
-- database | user | cl_active | cl_waiting | sv_active | sv_idle | pool_mode

-- 전체 통계
SHOW STATS;
-- total_xact_count, total_query_count, avg_xact_time, avg_query_time

-- 서버 연결 상태
SHOW SERVERS;

-- 클라이언트 연결 상태
SHOW CLIENTS;

-- 설정 리로드
RELOAD;

-- 데이터베이스 일시 중지 (유지보수)
PAUSE myapp;
RESUME myapp;

HikariCP vs PgBouncer 선택

항목HikariCPPgBouncer
위치애플리케이션 내장독립 프록시
언어Java/JVM모든 언어
다중화인스턴스별 풀전체 인스턴스 연결 통합
적합한 경우단일 앱, JVM 환경마이크로서비스, 다중 앱
  • 마이크로서비스에서 인스턴스 수 x 풀 크기가 PostgreSQL max_connections를 초과하면 PgBouncer가 필수입니다
  • PgBouncer transaction 모드에서는 PREPARED STATEMENT를 사용할 수 없으므로 ORM 설정을 확인하세요
  • HikariCP의 max-lifetime은 PostgreSQL의 idle_in_transaction_session_timeout보다 짧게 설정하세요
  • 두 가지를 함께 사용할 수도 있습니다: HikariCP(앱 측 풀) -> PgBouncer(서버 측 다중화) -> PostgreSQL

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록