※ PostgreSQL: Connection Pool.
안녕하세요. 듀스트림입니다.
제가 운영 중인 OLTP 시스템은 초당 5,000건 이상의 트랜잭션을 처리하고 있습니다.
이런 환경에서 클라이언트가 매번 DBMS에 직접 연결을 맺는다면, 쿼리 실행 이전에 커넥션 생성/해제 비용만으로도 막대한 리소스가 낭비될 수 있습니다.
오늘 포스팅에서는 커넥션 풀이 필요한 이유와 실제로 어떤 비용이 줄어드는지를 간단히 정리해 보겠습니다.
커넥션 풀이란?
커넥션을 미리 만들어 두고 필요할 때 재사용하는 기술입니다.
쉽게 말하면 상담 창구를 상시 열어두고, 고객이 왔을 때 열려 있는 빈 상담 창구에서 바로 상담을 시작하는 것과 같습니다.
1. 커넥션을 맺는 비용
PostgreSQL에서 클라이언트가 새로운 커넥션을 만들면 내부적으로 다음 과정이 발생합니다.
- TCP 핸드셰이크
- 클라이언트 ↔ 서버 간 3-way handshake. 네트워크 지연(RTT)과 OS 커널 컨텍스트 스위칭 발생
- 인증 과정
- 패스워드/SCRAM 인증 등 암호화 계산 필요
- 세션 초기화
- search_path, GUC, 임시 버퍼 초기화
- 프로세스 생성
- PostgreSQL은 프로세스 기반(fork) 구조이므로, 새로운 백엔드 프로세스 생성 비용 발생
- 쿼리 실행
- 실제 SQL 처리 비용
일반적으로 커넥션 생성에는 1~5ms 정도가 소요되며, TPS가 높은 환경에서는 이 비용이 누적되어 전체 성능 저하의 원인이 됩니다.
2. 커넥션 풀 활용
커넥션 풀(Pgpool-II, PgBouncer 등)은 DB 서버와 미리 N개의 커넥션을 만들어 두고 재사용합니다.
- 초기 커넥션 생성: 서버와 N개의 커넥션 생성 (한 번만 발생)
- 클라이언트 요청: 기존 커넥션 할당 → fork/handshake 비용 거의 없음
- 세션 초기화 최적화: Prepared Statement, GUC 등 재사용 가능
- 종료 시 연결 반환: DB 연결은 유지, idle 상태
| 항목 | 매번 커넥션 | 커넥션 풀 |
| 초기 연결 비용 | 요청마다 2~5ms | 한 번만 발생 |
| TCP handshake | 요청마다 | 초기 생성 시만 |
| 인증 비용 | 요청마다 | 초기 생성 시만 |
| CPU/메모리 | 높음 | 낮음 (idle connection 유지 비용만 존재) |
| 동시 접속자 처리 | 부담 큼 | 안정적 처리 가능 |
• 커넥션 생성: 1~5ms/conn
• 풀 재사용: 0.1ms/conn
→ TPS가 높은 환경에서 커넥션 풀 사용 시, CPU와 Latency에서 10~30% 성능 개선 효과가 있습니다.
동시 트랜잭션 수 = TPS * 평균 트랜잭션 시간
3. PostgreSQL 대표 커넥션 풀러
| 풀 | 특징 | 사용 시점 |
| PgBouncer | Lightweight, Session/Transaction pooling, latency 최소화 | 빠른 쿼리 반복, 웹 API |
| Pgpool-II | 로드밸런싱, Failover, Replication 지원, Connection pooling | HA/복제 환경, 장애 대응 필요 |
단순 TPS 개선에는 PgBouncer가 가볍고 좋습니다.
3.1 PgBouncer
PgBouncer는 경량화된 커넥션 풀러로, 클라이언트와 PostgreSQL 서버 사이에서 중개자 역할을 합니다.
클라이언트는 PgBouncer에 연결하고, PgBouncer는 내부적으로 PostgreSQL 서버와의 연결을 관리합니다.
이러한 방식으로 클라이언트는 PostgreSQL 서버와 직접 연결하는 것처럼 동작하지만, 실제로는 PgBouncer를 통해 연결됩니다.
PgBouncer는 세 가지 풀링 모드를 제공합니다:
- Session Pooling: 클라이언트와의 연결이 유지되는 동안 하나의 PostgreSQL 서버 연결을 할당합니다.
- Transaction Pooling: 트랜잭션 단위로 PostgreSQL 서버 연결을 할당하고, 트랜잭션 종료 시 연결을 반환합니다.
- Statement Pooling: 쿼리 단위로 PostgreSQL 서버 연결을 할당하고, 쿼리 종료 시 연결을 반환합니다.
참고:
PostgreSQL Connection Pooling: Part 2 – PgBouncer
PgBouncer is the most popular PostgreSQL connection pooler. In this post, we explain how PgBouncer works, its pros and cons, and how to setup connection pooling
scalegrid.io
3.2 Pgpool-II
Pgpool-II는 PostgreSQL 서버와 클라이언트 사이에 위치한 미들웨어로 커넥션 풀링 외에도 로드 밸런싱, 장애 조치(failover), 쿼리 캐싱 등의 기능을 제공합니다.
Pgpool-II는 클라이언트의 요청을 받아 내부적으로 PostgreSQL 서버와의 연결을 관리합니다.
Pgpool-II는 기본적으로 세션 기반 커넥션 풀링을 사용하며, 클라이언트와의 연결이 유지되는 동안 하나의 PostgreSQL 서버 연결을 할당합니다.
참고:
PostgreSQL Connection Pooling: Part 3 – Pgpool-II
Learn more about Pgpool-II connection pooling for PostgreSQL. How to set up Pgpool-II, how it works, and limitations to see if it's right for your application.
scalegrid.io
요약
| 항목 | PgBouncer | Pgpool-II |
| Pooling 방식 | • Session / Transaction / Statement | • Session 기반 |
| Prepared Statement 재사용 | • Session pooling 시 가능 • Transaction pooling 시 불가 |
• Session 연결 유지 시 가능(세션 기반 풀링) |
| Failover / Load balancing | • Prepared Statement는 유지 불가 (재준비 필요) | • Failover 발생 시 재준비 필요 • 동일 세션 내에서는 재사용 가능 |
| 특징 | • 경량, 빠름 | • HA/로드밸런싱 포함, 기능 많음 |
- Pgpool-II 는 max_connections * DB 노드 수 ≥ num_init_children * max_pool.
- PgBouncer는 pool_size가 각 DB 노드 별로 적용(노드 수에 따른 조정 필요없음).
- Session Pooling: DB 연결 1:1, max_connections ≥ pool_size ≥ 예상 동시 클라이언트 수.
- Transaction / Statement Pooling: DB 연결 재사용, max_connections ≥ pool_size, 풀링 덕분에 클라이언트 수보다 작아도 가능.
- max_client_conn: PgBouncer가 동시에 허용할 클라이언트 연결 수 상한, 큐 길이 제한 역할.
+ MySQL/MariaDB에는 ProxySQL, MaxScale 등이 있습니다.
결론은 TPS가 높은 OLTP 환경에서는, 매번 커넥션을 생성하는 방식이 CPU와 레이턴시 부담을 크게 증가시키므로 커넥션 풀 사용을 권장드립니다.
Connection Pooling
Pgpool-II maintains established connections to the PostgreSQL servers, and reuses them whenever a new connection with the same properties (i.e. user name, database, protocol version) comes in. It reduces the connection overhead, and improves system's overa
www.pgpool.net
PgBouncer config
Website © 2025 PgBouncer Authors. Design © Scott Emmons
www.pgbouncer.org
+ 커넥션 풀과 Prepared Statement
- PostgreSQL 커넥션 풀(PgBouncer, Pgpool-II 등)을 사용하면 Prepared Statement는 커넥션 단위로 존재합니다. (Prepared Statement는 클라이언트와 DB 서버 간 연결(세션)에 독립적으로 존재 (풀이 아니라 세션에 종속))
- 풀에서 가져온 커넥션에 이미 존재하는 Prepared Statement를 그대로 사용할 수 있지만, 통계 갱신 후 실행 시 plan은 다시 생성됩니다.
- 풀 자체가 Prepared Statement 캐시를 관리하지 않습니다.
[클라이언트 A] ──────┐
│
▼
[커넥션 풀] ──> [DB 서버 세션 1] ← Prepared Statement 있음
│
└─> [DB 서버 세션 2] ← Prepared Statement 없음
- 커넥션 풀 안의 각 DB 세션이 Prepared Statement를 가지는 주체
- 클라이언트가 풀에서 가져온 커넥션이 세션 1이면 재사용 가능
- 세션 2를 가져오면 재준비 필요
오늘은 여기까지~
'PostgreSQL' 카테고리의 다른 글
| PostgreSQL: ERROR [40001]: canceling statement due to conflict with recovery (0) | 2025.10.20 |
|---|---|
| PostgreSQL: Skip Locked (0) | 2025.09.17 |
| PostgreSQL: LIKE vs ILIKE (0) | 2025.09.12 |
| PostgreSQL: 인덱스 - 연산자 클래스 (0) | 2025.09.10 |
| PostgreSQL: 데이터베이스 레벨 접근 권한 설정 (0) | 2025.09.09 |