커넥션 풀
미리 일정 수의 DB 커넥션을 생성해 풀(Pool)에 보관하고, 요청 시 재사용하여 연결 생성/해제 비용을 줄이는 기법입니다.
동작 원리
[커넥션 풀 없이]
요청 → 커넥션 생성 → SQL 실행 → 커넥션 해제 → 응답
(TCP 3-way handshake + 인증) (매번 반복, 느림)
[커넥션 풀 사용]
애플리케이션 시작 시 커넥션 N개 미리 생성
요청1 → 풀에서 커넥션 빌림 → SQL 실행 → 풀에 반납 → 응답
요청2 → 풀에서 커넥션 빌림 → SQL 실행 → 풀에 반납 → 응답
(생성/해제 비용 없음, 빠름)
[ Connection Pool ]
┌──────────────────────────────────┐
│ ● 사용 중 ○ 대기 중(idle) │
│ │
│ ● conn1 → [요청 A 처리 중] │
│ ● conn2 → [요청 B 처리 중] │
│ ○ conn3 → [대기] │
│ ○ conn4 → [대기] │
│ ○ conn5 → [대기] │
│ │
│ 요청 C 도착 → conn3 빌려감 │
│ 요청 A 완료 → conn1 반납 │
└──────────────────────────────────┘
커넥션 풀이 필요한 이유
| 문제 | 풀 미사용 | 풀 사용 |
|---|---|---|
| 연결 생성 비용 | 매 요청마다 TCP handshake + 인증 (수십 ms) | 최초 1회만 생성, 이후 재사용 |
| 자원 고갈 | 동시 요청 폭증 시 커넥션 무한 생성 → DB 과부하 | 최대 커넥션 수 제한으로 DB 보호 |
| 응답 시간 | 연결 생성 지연이 매번 발생 | 즉시 사용 가능 |
| 메모리 사용 | 커넥션마다 DB 측 메모리 할당/해제 반복 | 고정된 수만 유지하여 안정적 |
Java 예시 (HikariCP — Spring Boot 기본)
설정
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: secret
hikari:
minimum-idle: 5 # 최소 유지 커넥션 수
maximum-pool-size: 10 # 최대 커넥션 수
connection-timeout: 3000 # 커넥션 획득 대기 시간 (ms)
idle-timeout: 600000 # 유휴 커넥션 유지 시간 (ms)
max-lifetime: 1800000 # 커넥션 최대 수명 (ms)
leak-detection-threshold: 2000 # 커넥션 누수 감지 (ms)
직접 생성
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("root");
config.setPassword("secret");
config.setMaximumPoolSize(10);
config.setMinimumIdle(5);
config.setConnectionTimeout(3000);
HikariDataSource dataSource = new HikariDataSource(config);
// 커넥션 사용
try (Connection conn = dataSource.getConnection()) {
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
ps.setLong(1, 1L);
ResultSet rs = ps.executeQuery();
// 처리 로직
} // try-with-resources → 자동으로 풀에 반납 (close ≠ 해제, 반납)
주요 설정 값 가이드
| 설정 | 설명 | 권장 값 |
|---|---|---|
| maximum-pool-size | 풀이 보유할 최대 커넥션 수 | 10~20 (서버 규모에 따라 조절) |
| minimum-idle | 유휴 상태로 유지할 최소 커넥션 수 | maximum-pool-size와 동일 권장 |
| connection-timeout | 풀에서 커넥션을 얻기까지 대기 시간 | 3000ms (3초) |
| idle-timeout | 유휴 커넥션이 풀에서 제거되기까지 시간 | 600000ms (10분) |
| max-lifetime | 커넥션의 최대 수명 (DB wait_timeout보다 짧게) | 1800000ms (30분) |
적정 풀 사이즈 공식
최적 풀 사이즈 = CPU 코어 수 × (1 + DB 대기시간 / CPU 처리시간)
HikariCP 공식 권장:
pool size = (core_count * 2) + effective_spindle_count
예: 4코어, SSD 1개
→ (4 × 2) + 1 = 9~10
주의: 풀 사이즈를 무작정 늘리면 오히려 성능이 저하됩니다. DB 커넥션은 공유 자원이므로 과도한 커넥션은 컨텍스트 스위칭, 메모리 낭비, DB 부하를 유발합니다.
커넥션 풀 동작 흐름
요청 도착
│
▼
풀에 유휴 커넥션 있는가? ──── YES ──→ 커넥션 빌려줌 → SQL 실행 → 반납
│
NO
│
▼
현재 커넥션 수 < max-pool-size? ── YES ──→ 새 커넥션 생성 → 빌려줌
│
NO
│
▼
connection-timeout까지 대기
│
├── 대기 중 반납된 커넥션 있음 → 빌려줌
└── 타임아웃 초과 → ConnectionTimeoutException 발생
주요 커넥션 풀 라이브러리 비교
| 항목 | HikariCP | DBCP2 | Tomcat Pool |
|---|---|---|---|
| 성능 | 가장 빠름 | 보통 | 보통 |
| Spring Boot 기본 | O (2.0+) | X | X |
| 모니터링 | JMX, Micrometer 지원 | JMX | JMX |
| 커넥션 검증 | 경량 (Connection.isValid) | 쿼리 기반 | 쿼리 기반 |
| 바이트코드 최적화 | O (FastList, ConcurrentBag) | X | X |
실무 팁: 커넥션 누수는 장애의 주요 원인입니다.
leak-detection-threshold를 설정하여 커넥션을 빌린 후 일정 시간 내 반납하지 않으면 경고 로그를 남기도록 하고, 반드시 try-with-resources로 커넥션 반납을 보장하세요.