드루이드 연결 풀 메모리 관리: OOM 오류 방지를 위한 핵심 설정
애플리케이션이 갑자기 중단되고 로그에 OutOfMemoryError가 가득 찬 경험을 해본 적이 있나요? 알리클라우드 DataWorks 팀이 개발한 데이터베이스 연결 풀인 드루이드는 강력한 메모리 관리 메커니즘을 제공하지만, 잘못된 설정은 여전히 메모리 누수와 OOM을 유발할 수 있습니다. 본문에서는 안정적인 연결 풀을 구축하는 데 도움이 될 7가지 핵심 설정 항목을 상세히 설명합니다.
본문을 통해 다음을 학습할 수 있습니다:
- 풀 크기 제어를 통한 메모리 오버플로우 방지 방법
- 유휴 연결의 지능적 관리 전략
- 연결 누수 방지를 위한 시간 초기 회수 메커니즘
- 프로덕션 환경 최적 설정 사례
연결 풀 용량 계획: maxTotal과 minIdle의 최적 비율
연결 풀의 핵심 역할은 데이터베이스 부하와 애플리케이션 성능을 균형 잡는 것이며, maxTotal(최대 활성 연결 수)과 minIdle(최소 유휴 연결 수)는 메모리 사용량을 제어하는 첫 번째 방어선입니다.
maxTotal: 메모리 보호 안전 밸브
maxTotal은 연결 풀이 생성할 수 있는 최대 연결 수를 정의하며, 이는 직접 메모리 사용량 상한을 결정합니다. 소스 코드 core/src/main/java/com/alibaba/druid/pool/DruidDataSource.java에서 이 매개변수가 설정될 때 범위 검사가 수행되는 것을 볼 수 있습니다:
if (maxTotal < minIdle) {
throw new IllegalArgumentException("maxTotal less than minIdle, " + maxTotal + " < " + this.minIdle);
}
추천 설정: 데이터베이스 성능과 서버 메모리를 기반으로 계산하며, 참고 공식은 다음과 같습니다:
maxTotal = (서버 사용 가능 메모리MB - 다른 서비스 점유량) / 각 연결 예상 메모리MB
예를 들어 8GB 서버에 연결 풀에 4GB를 할당할 때, 각 연결이 10MB를 사용한다면 400으로 설정할 수 있습니다.
minIdle: 성능과 메모리의 균형
minIdle은 연결 풀이 유지해야 하는 최소 유휴 연결 수를 지정합니다. 기본 설정 방식은 테스트 코드 core/src/test/java/com/alibaba/druid/pool/Demo0.java에서 확인할 수 있습니다:
private int initSize = 10;
private int minIdle = 1;
private int maxTotal = 12;
dataSource.setInitialSize(initSize);
dataSource.setMaxTotal(maxTotal);
dataSource.setMinIdle(minIdle);
최적 사례: maxTotal의 1/4~1/2로 설정하여 요청에 빠르게 응답하면서도 유휴 연결로 인한 메모리 낭비를 방지합니다.
연결 생명주기 관리: evictionIntervalMillis와 유휴 시간 제한
풀 크기를 올바르게 설정하더라도 장기간 유휴 상태인 연결은 여전히 메모리를 소모합니다. 드루이드는 유휴 연정을 정리하기 위해 두 가지 메커니즘을 제공합니다:
주기적 검사 메커니즘: evictionIntervalMillis
이 매개변수는 연결 회수 스레드의 실행 주기를 밀리초 단위로 정의합니다. 기본값은 60000ms(1분)이며, core/src/main/java/com/alibaba/druid/pool/DruidDataSource.java의 DestroyTask를 통해 주기적 검사가 구현됩니다.
유휴 시간 초과 전략: minIdleTimeMillis와 maxIdleTimeMillis
minIdleTimeMillis: 연결이 유휴 상태로 유지되어 제거되지 않는 최소 시간(기본값 30분)maxIdleTimeMillis: 연결 최대 유휴 시간(기본값 7시간)
druid-spring-boot-3-starter/src/main/java/com/alibaba/druid/spring/boot3/autoconfigure/DruidDataSourceWrapper.java에서 이 두 매개변수의 관계가 특별히 처리됩니다:
* 'maxIdleTimeMillis < minIdleTimeMillis' 검증을 무시합니다,
public void setMaxIdleTimeMillis(long maxIdleTimeMillis) {
super.setMaxIdleTimeMillis(maxIdleTimeMillis);
}
추천 설정:
# 30초마다 한 번 검사
evictionIntervalMillis=30000
# 3분 유휴 후 제거 가능
minIdleTimeMillis=180000
# 10분 유휴 시 강제 제거
maxIdleTimeMillis=600000
연결 누수 방지: leakDetectionTimeout의 중요한 역할
애플리케이션이 연결을 올바르게 닫지 않으면 연결 누수가 발생하여 연결 풀 자원이 고갈될 수 있습니다. 드루이드의 leakDetection 시리즈 매개변수는 이를 방지하는 안전망을 제공합니다.
테스트 코드 core/src/test/java/com/alibaba/druid/bvt/pool/TestLeakDetection.java에서 완전한 누수 방지 메커니즘을 확인할 수 있습니다. 활성화 방법:
dataSource.setLeakDetection(true); // 누수 감지 활성화
dataSource.setLeakDetectionTimeout(300); // 5분 사용 없으면 누수로 간주
dataSource.setLogLeak(true); // 누수 로그 기록
프로덕션 환경 권장 사항:
leakDetectionTimeout을 최장 실행 시간의 1.5배로 설정logLeak과 함께 스택 정보를 기록하여 누수 지점 식별- 비상 상황이 아닌 경우 로그 경고만 사용하고 직접 연결 종료는 피함
초기화 최적화: initSize와 acquireWait의 사전 전략
연결 풀 초기화 전략이 부적절하면 애플리케이션 시작이 느리거나 메모리 피크가 과도하게 발생합니다.
initSize: 시작 시 사전 연결 생성
initSize는 시작 시 생성되는 연결 수를 지정하며, 초기화 로직은 테스트 케이스 core/src/test/java/com/alibaba/druid/pool/Case0.java에서 검증됩니다:
private int initSize = 1;
private int maxTotal = 2;
dataSource.setInitialSize(initSize);
dataSource.setMaxTotal(maxTotal);
최적 사례: minIdle 값으로 설정하여 시작 후 즉시 대량 연결 생성으로 인한 메모리 변동을 방지합니다.
acquireWait: 요청 대기 메커니즘
모든 연결이 사용 중일 때 acquireWait는 새 요청에 대한 최대 대기 시간(밀리초)을 정의합니다. 소스 코드 core/src/main/java/com/alibaba/druid/pool/DruidDataSource.java에서 대기 잠금을 통해 구현됩니다:
if (acquireWait > 0) {
notEmptyWaitNanos += (System.nanoTime() - beginNanos);
notEmpty.await(timeout, TimeUnit.MILLISECONDS);
}
추천 설정: 3000ms(3초), 너무 짧으면 빈번한 예외 발생, 너무 길면 사용자 경험에 영향
프로덕션 환경 설정 템플릿
위 분석을 종합하여 프로덕션급 설정 예제:
# 핵심 연결 관리
spring.datasource.druid.max-total=200 # 최대 연결 수
spring.datasource.druid.min-idle=50 # 최소 유휴 연결
spring.datasource.druid.initial-size=50 # 초기 연결 수
# 생명주기 관리
spring.datasource.druid.eviction-interval-millis=30000 # 30초마다 검사
spring.datasource.druid.min-idle-time-millis=180000 # 3분 유휴 후 제거 가능
spring.datasource.druid.max-idle-time-millis=600000 # 10분 유휴 시 강제 제거
# 보호 기능
spring.datasource.druid.leak-detection=true # 누수 감지 활성화
spring.datasource.druid.leak-detection-timeout=300 # 5분 초과 시 누수로 간주
spring.datasource.druid.log-leak=true # 누수 로그 기록
# 대기 전략
spring.datasource.druid.acquire-wait=3000 # 최대 3초 대기
모니터링 및 튜닝: 드루이드 내장 도구
최적화 후 druid-admin/ 모듈을 통해 연결 풀 상태를 모니터링할 수 있으며, 주요 지표는 다음과 같습니다:
activeCount: 현재 활성 연결 수idleCount: 유휴 연결 수activePeak: 피크 활성 연결leakedCount: 회수된 누수 연결 수
이러한 지표를 정기적으로 분석하고 비즈니스 변화에 따라 설정을 조정해야만 장기적으로 메모리 안정성을 유지할 수 있습니다.
결론 및 전망
드루이드 연결 풀의 메모리 관리는 시스템 엔지니어링의 일부로, 다음을 종합적으로 고려해야 합니다:
- 풀 크기(maxTotal/minIdle)를 합리적으로 설정하여 메모리 상한 제어
- 유휴 시간 초과 전략을 구성하여 자원 자동 해제
- 누수 감지 메커니즘을 최후의 방어선으로 활성화
- 모니터링을 통해 지속적으로 매개변수 최적화
드루이드 버전 업데이트에 따라 doc/ha-datasource.md에 언급된 고가용성 기능도 메모리 설정에 영향을 미칩니다. 공식 문서를 정기적으로 확인하여 설정 최적 사례를 최신 상태로 유지하는 것이 좋습니다.
이러한 설정을 숙달하면 애플리케이션이 OOM을 효과적으로 방지하면서 데이터베이스 액세스의 높은 성능과 안정성을 유지할 수 있습니다.