99%가 모른다는 DB Connection 누수 문제
김동호 · 컬리 · 컬리 기술 블로그
소개
DB Connection과 Garbage Collector의 관계를 중심으로 mysql-connector-j 사용 시 발생할 수 있는 메모리 누수를 탐지하고 해결한 경험을 공유합니다.
AI 핵심 요약
MariaDB JDBC 드라이버를 MySQL로 바꾼 뒤 EC2에서 OOM이 반복되자, 팀은 Heap Dump를 분석해 AbandonedConnectionCleanupThread가 보유한 PhantomReference와 Connection 객체가 누적된 것을 확인했습니다. 이 스레드는 ReferenceQueue를 감시하며 버려진 Connection의 네트워크 자원을 정리하지만, 단일 스레드가 한 번에 하나씩 처리해 네트워크 병목이 생길 수 있습니다. HikariCP의 max-lifetime을 50초로 짧게 설정한 탓에 Connection 재생성 속도가 정리 속도를 앞질렀고, mysql-connector-j를 8.0.22 이상으로 올린 뒤 정리 스레드를 비활성화하는 JVM 옵션을 적용했습니다. 적용 후 Heap Dump에서 해당 메모리 점유가 사라졌고, 장애 분석에는 트래픽 지표뿐 아니라 Heap Dump와 객체 참조 관계를 살펴보는 것이 중요하다는 교훈을 얻었습니다.
- OOM이 발생하면 트래픽 증가만 의심하지 말고 Heap Dump에서 메모리를 점유하는 객체와 참조 경로를 확인하세요.
- mysql-connector-j의 AbandonedConnectionCleanupThread는 PhantomReference와 ReferenceQueue를 이용해 Connection의 네트워크 자원을 정리합니다.
- 단일 스레드가 네트워크 자원을 하나씩 정리하므로 Connection 재생성 속도가 처리 속도를 앞서면 참조 객체가 누적될 수 있습니다.
- Connection의 max-lifetime을 짧게 설정할 때는 장애 조치 요구와 정리 작업의 부하를 함께 고려해야 합니다.
- mysql-connector-j 8.0.22 이상에서는 -Dcom.mysql.cj.disableAbandonedConnectionCleanup=true 옵션으로 정리 스레드를 비활성화할 수 있습니다.
비슷한 학습 자료
토스ㅣSLASH 22 - Java Native Memory Leak 원인을 찾아서
SLASH · YouTube
Spark Connect on Kubernetes #1: 견고한 Spark Connect 만들기
박지원 · 토스 기술 블로그
Spring JDBC 성능 문제, 네트워크 분석으로 파악하기
강민주 · 토스 기술 블로그
데이터가 있었는데요, 아니 없어요
호선우 · 컬리 기술 블로그
JPA 덕분에 DB에서 삽질한 이야기
박제희 · 컬리 기술 블로그
Kafka Connect로 DB 데이터 쉽게 연동하기
김소라 · 컬리 기술 블로그