커넥션 풀 사이징이 중요한 이유
HikariCP는 Spring Boot의 기본 커넥션 풀이지만, 기본 설정(maximumPoolSize=10)을 그대로 사용하면 동시 요청이 몰릴 때 커넥션 고갈이 발생합니다. 반대로 풀을 너무 크게 잡으면 DB 서버의 연결 한도를 초과하거나 컨텍스트 스위칭 오버헤드가 증가합니다.
Spring Boot HikariCP 커넥션 풀에서 기본 설정을 다뤘다면, 이 글에서는 운영 환경 튜닝, 모니터링, 장애 대응에 집중합니다.
최적 풀 사이즈 공식
HikariCP 공식 문서가 권장하는 풀 사이즈 공식입니다:
connections = (core_count * 2) + effective_spindle_count
SSD 환경(spindle=0)에서 4코어 서버라면 약 8~10개가 적정입니다. 직관과 달리 풀 사이즈를 늘린다고 성능이 올라가지 않습니다.
| 서버 스펙 | 권장 풀 사이즈 | 비고 |
|---|---|---|
| 2코어 SSD | 5~6 | 소규모 서비스 |
| 4코어 SSD | 8~10 | 일반적인 운영 환경 |
| 8코어 SSD | 15~20 | 대규모 트래픽 |
| 다중 인스턴스 (4개) | 인스턴스당 5~8 | DB max_connections / 인스턴스 수 |
핵심 원칙: DB의 max_connections를 전체 애플리케이션 인스턴스의 풀 사이즈 합으로 나눠야 합니다. MySQL 기본 151개에서 시스템 예약 20개를 빼면 131개. 인스턴스 4개면 인스턴스당 최대 32개입니다.
운영 환경 설정
# application.yml
spring:
datasource:
hikari:
# 풀 사이즈
maximum-pool-size: 10
minimum-idle: 5 # idle 커넥션 최소 유지 수
# 타임아웃
connection-timeout: 3000 # 커넥션 획득 대기 (3초, 기본 30초는 너무 김)
idle-timeout: 600000 # 유휴 커넥션 제거 (10분)
max-lifetime: 1800000 # 커넥션 최대 수명 (30분)
validation-timeout: 3000 # 유효성 검증 타임아웃
# 커넥션 검증
connection-test-query: SELECT 1
# 누수 감지
leak-detection-threshold: 10000 # 10초 이상 반환 안 하면 경고
# 풀 이름 (모니터링용)
pool-name: MainHikariPool
# 메트릭
register-mbeans: true
주요 설정 해설
| 설정 | 기본값 | 권장 | 이유 |
|---|---|---|---|
| connection-timeout | 30000ms | 3000ms | 빠른 실패가 사용자 경험에 유리 |
| max-lifetime | 1800000ms | 1800000ms | DB wait_timeout보다 짧아야 함 |
| minimum-idle | = max | max의 50% | 유휴 시 리소스 절약 |
| leak-detection-threshold | 0(비활성) | 10000ms | 커넥션 누수 조기 발견 |
커넥션 누수 감지와 디버깅
leak-detection-threshold를 설정하면 HikariCP가 커넥션을 빌려간 후 지정 시간 내 반환하지 않으면 스택트레이스를 로그에 출력합니다.
// ⚠️ 커넥션 누수 예시: try-with-resources 미사용
public List<Order> findOrders() {
Connection conn = dataSource.getConnection(); // 빌려감
// ... 쿼리 실행
// conn.close() 호출 안 함 → 누수!
return orders;
}
// ✅ 올바른 사용
public List<Order> findOrders() {
try (Connection conn = dataSource.getConnection()) {
// ... 쿼리 실행
return orders;
} // 자동 반환
}
JPA/Spring Data를 사용하면 대부분 자동 관리되지만, @Transactional 범위가 넓거나 외부 API 호출을 트랜잭션 안에서 하면 사실상 누수와 같은 효과가 발생합니다.
// ⚠️ 안티패턴: 트랜잭션 안에서 외부 API 호출
@Transactional
public void processOrder(Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow();
// 외부 결제 API 호출 (3~5초 소요) → 커넥션 점유 중!
PaymentResult result = paymentClient.charge(order.getAmount());
order.complete(result.getTransactionId());
orderRepository.save(order);
}
// ✅ 개선: 외부 호출은 트랜잭션 밖에서
public void processOrder(Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow();
// 트랜잭션 밖에서 외부 호출
PaymentResult result = paymentClient.charge(order.getAmount());
// 짧은 트랜잭션으로 DB 작업만
orderService.completeOrder(orderId, result.getTransactionId());
}
Micrometer 메트릭 모니터링
Spring Micrometer 커스텀 메트릭과 연동하면 HikariCP 상태를 실시간 모니터링할 수 있습니다.
# application.yml
management:
metrics:
enable:
hikaricp: true
endpoints:
web:
exposure:
include: health,metrics,prometheus
자동 등록되는 주요 메트릭:
| 메트릭 | 의미 | 알람 기준 |
|---|---|---|
hikaricp_connections_active |
사용 중인 커넥션 | > max의 80% |
hikaricp_connections_pending |
대기 중인 스레드 | > 0이 지속되면 위험 |
hikaricp_connections_idle |
유휴 커넥션 | 0이면 풀 부족 |
hikaricp_connections_timeout_total |
타임아웃 발생 횟수 | > 0이면 즉시 조사 |
hikaricp_connections_creation_seconds |
커넥션 생성 시간 | > 1초면 네트워크 점검 |
# Prometheus 알람 룰 예시
groups:
- name: hikaricp
rules:
- alert: HikariPoolExhausted
expr: hikaricp_connections_pending{pool="MainHikariPool"} > 0
for: 1m
labels:
severity: warning
annotations:
summary: "커넥션 풀 대기열 발생"
- alert: HikariConnectionTimeout
expr: rate(hikaricp_connections_timeout_total[5m]) > 0
labels:
severity: critical
annotations:
summary: "커넥션 획득 타임아웃 발생"
다중 DataSource 풀 분리
읽기/쓰기 분리나 배치 전용 풀을 구성할 때 DataSource를 분리합니다.
@Configuration
public class DataSourceConfig {
@Bean
@Primary
@ConfigurationProperties("spring.datasource.hikari")
public HikariDataSource primaryDataSource() {
return DataSourceBuilder.create()
.type(HikariDataSource.class)
.build();
}
@Bean("batchDataSource")
public HikariDataSource batchDataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://replica:3306/mydb");
config.setUsername("batch_user");
config.setPassword("password");
config.setPoolName("BatchHikariPool");
config.setMaximumPoolSize(3); // 배치는 적은 커넥션
config.setConnectionTimeout(10000); // 배치는 대기 허용
config.setMaxLifetime(3600000); // 1시간
return new HikariDataSource(config);
}
}
정리
HikariCP 튜닝의 핵심은 풀 사이즈를 작게 유지하고(cores×2), connection-timeout을 짧게(3초) 설정하여 빠르게 실패하는 것입니다. leak-detection-threshold로 누수를 조기 발견하고, Micrometer 메트릭의 pending/timeout을 모니터링하면 커넥션 풀 관련 장애를 예방할 수 있습니다. 트랜잭션 범위를 최소화하여 커넥션 점유 시간을 줄이는 것이 가장 효과적인 최적화입니다.