HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
✍🏻
Learnary (learn - diary)
/
Connection Pool과 HikariCP

Connection Pool과 HikariCP

progress
Done
Tags
Database
 
데이터베이스 커넥션DBCPDBCP 구현하여 포퍼먼스 비교해보기HikariCP적정 커넥션 풀 사이즈 HikaricCP DeadLockRefer
 

데이터베이스 커넥션

웹 애플리케이션과 데이터베이스는 서로 다른 시스템이며 웹 애플리케이션은 내부적으로 드라이버를 사용하여 데이터베이스에 연결해야 한다.
 
연결하는 과정에 대한 생명주기는 보통 이렇다.
  1. 데이터베이스 드라이버를 사용하여 데이터베이서 연결
  1. 읽고 쓰기 위한 TCP 소켓 열기 (3-way)
  1. TCP 소켓을 통해 데이터 통신
  1. 데이터베이스 연결 닫기
  1. TCP 소켓 닫기 (4-way)
 

DBCP

 
사용자로부터 웹 애플리케이션에 요청이 들어올때마다 DB와 연결을 수립하고 해제 하는 비용은 엄청 빘ㅅㅅㅅㅅㅅ산 자원이다. 그래서 데이터베이스 커넥션 풀이라는 기술이 출연하였고 이미 여러개의 DB 연결을 해놓은 여러개의 자원을 로드해 사용하는 식이다. DBCP의 아이디어는 지속적으로 연결을 열고 닫고 하는 오버헤드를 방지하기 위해 출연하였다.
 
notion image
데이터베이스 커넥션 풀에는 사전에 데이터베이스와 연결이 수립된 상태의 다수의 커넥션이 존재한다.
기본적으로 스프링부트에서는 10개가 Default이다.
커넥션들은 데이터 베이스 요청이 들어올때마다 연결 수립, 닫기 과정이 일어나지 않고 항상 열린 상태를 유지한다.
이로써 우리는 DB와 연결 수립, 해제 하는 과정에 대한 오버헤드를 줄일 수 있게되었다.
 
 

DBCP 구현하여 포퍼먼스 비교해보기

 
dbcp 성능 테스트
 
dbcp 사용 전 수행시간: 6371 밀리초
dbcp 사용 후 수행시간: 284 밀리초
 
엄청난 차이가 나는 것을 볼 수 있다.
무려 30배 가량 차이가 난다.
 
 

HikariCP

데이터베이스 커넥션 풀 프레임워크는 대표적으로 Apache Commons DBCP, Tomcat DBCP, HikariCP, Oracle UCP 등이 있다. 이번 포스팅에서는 가장 많이 사용되는 HikariCP에 대해 알아본다.
스프링부트로 개발을 해본 사람이라면, HikariCP가 무엇인지 이름은 들어봤을 것이다. 스프링부트로 만든 애플리케이션을 기동하면 아래와 같은 로그가 출력되기 때문이다.
com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
HikariCP는 스프링부트에 기본으로 내장되어 있는 JDBC 데이터베이스 커넥션 풀링 프레임워크이다. 스프링부트는 HikariCP를 사용할 수 있는 상황에서는 항상 HikariCP를 선택한다고 한다. HikariCP를 사용할 수 없는 경우 Tomcat DBCP → Apachde Commons DBCP → Oracle UCP 순으로 선택한다고 한다 (참고).
 
스프링부트는 왜 HikariCP를 기본 내장하고, 최우선으로 선택하는걸까? HikariCP의 성능이 압도적으로 우수하기 때문이다. 아래 그래프는 HikariCP 팀에서 제공한 데이터베이스 커넥션 풀링 프레임워크들의 벤치마크 결과이다.
notion image
HikariCP는 바이트코드 수준까지 극단적으로 최적화 되어있다. 또한 미세한 최적화와 Collection 프레임워크를 영리하게 사용한 덕분에 위와 같은 벤치마크 결과가 나올 수 있었다.
 

적정 커넥션 풀 사이즈

HikariCP 공식문서에서는 아래와 같이 커넥션 풀 사이즈를 제안한다 (참고).
connection = ((core_count * 2) + effective_spindle_count)
core_count : cpu 코어수
effective_spindle_count: DB서버가 동시 관리할 수 있는 IO 개수
 

HikaricCP DeadLock

아래 내용은 우아한형제들 기술 블로그의 HikariCP Dead lock에서 벗어나기 (이론편) 를 참고
DBCP를 사용할 때 교착상태(deadlock)를 주의해야한다. 교착상태란 쓰레드가 서로의 DB Connection이 반납되기만을 무한정 대기하는 상황이다. 아주 간단한 deadlock 시나리오를 살펴보자.
  1. Thread-1이 작업을 수행하기 위해 2개의 DB Connection이 필요하다.
  1. DBCP의 사이즈는 1이다. (total = 1, active = 0, idle = 1, waiting = 0)
  1. Thread-1이 작업을 수행하기 위해 DBCP로부터 커넥션을 받아온다.
  1. DBCP에 현재 존재하는 커넥션은 1개이므로, 1개만 Thread-1에게 건네준다.
  1. Thread-1은 1개의 커넥션으로는 작업을 수행할 수 없으므로 다른 쓰레드로부터 1개의 커넥션이 반납될동안 기다린다.
  1. 하지만 DBCP의 상태는 total = 1, active = 1, idle = 0, waiting = 0 이므로 반납될 커넥션이 존재하지 않는다. 즉, Thread-1은 자기자신이 커넥션을 반납하는 것을 기다리는 상황이다. 즉, 데드락을 의미한다.
이 문제를 해결하기 위해서 HikariCP팀은 아래와 같은 DBCP 최소 사이즈 공식을 제안한다 (참고). 여기서 Tn은 WAS의 전체 쓰레드 개수, Cm은 쓰레드가 작업을 수행하기 위해 동시에 필요한 DB Connection의 개수를 의미한다.
notion image
 
위 공식을 적용해보면 쓰레드 최대 개수가 10개, 동시 필요 커넥션 수가 3이라고 해보자
21이라는 결과 값이 나오게 된다.
10개의 쓰레드에 모두 2개의 커넥션을 할당하면 1개의 커넥션이 풀에 남게 된다.
즉 최소한 하나의 쓰레드는 무조건 커넥션을 3개 확보하고 작업을 처리하고 커넥션을 반납한다.
따라서 데드락이 발생하지 않는다.
 
위공식의 결과값은 데드락이 발생하지 않을 최소 적정 값이다.
필요에 따라 결과값에 +알파 가 더 필요함에 유의해야 한다.
 
DBCP 사이즈를 여유있게 가져가면 되는 거 아닌가? 라고 생각할 수 있다. 하지만 한정된 메모리 자원으로 많이 생성해야 좋은 것은 아니다. 커넥션 또한 컴퓨터 세계에서 동작할 수 있는 객체라는 단위안에 포함되어 메모리에 상주하여 동작하기 때문이다.
따라서 다다익선이 제한된 메모리 측면과 함께 보면 트레이드 오프가 있는 것이다. 각 서비스를 지속적으로 모니터링하며 적정 결과에 +알파를 도입해야 한다.
 
 

Refer

  • https://www.baeldung.com/java-connection-pooling
  • https://steady-coding.tistory.com/564
  • https://www.jdatalab.com/information_system/2017/02/16/database-driver.html
  • https://github.com/brettwooldridge/HikariCP
  • https://steady-coding.tistory.com/564
  • https://hudi.blog/dbcp-and-hikaricp/