DEV ℧ Developer Diary

[Java] Hikari CP에 대해 알아보자. (1)

최근 회사에서 HikariCP로 인해 커넥션 누수 현상이 발생하고 있어 해당 원일을 찾고 수정 중에 있다. 먼저 원인 분석 및 해결을 위해서는 HikariCP에 대해 알고있어야 할 것 같아. HikariCP에 대해 공부해보고 간략하게 정리해보려고 한다.

HikariCP

HikariCP

공식 Github에서는 HikariCP를 이렇게 소개하고 있다.

Fast, simple, reliable. HikariCP is a “zero-overhead” production ready JDBC connection pool. At roughly 130Kb, the library is very light.

간단하게 번역해 보자면 빠르고 신뢰할 수 있으며, “오버 헤드가 없는” 가벼운 JDBC Connection Pool 라이브러리 로 볼 수 있다.

현재 HikariCP는 이 성능을 인정받아 Spring boot 2.0 버전에서 Tomcat DBCP => HikariCP로 전환이 되었다.

Connection Pool

HikariCP에 정리하기 전에 HikariCP가 관리하는 Connection Pool에 관하여 간략하게 설명 해보고자 한다.

Connection이란 요청이 들어왔을 때 애플리케이션과 데이터베이스간에 연결하여 쿼리를 실행해 데이터를 가져오고, 결과를 가져올시 종료하는 과정을 말한다.

Connection 과정에서 연결 및 종료하는데 자원이 많이 소모되므로, DB에서는 일정 수 Connection을 미리 만들어 제공해 주는 기능이 있다. 이렇게 미리 만들어 놓은 Connection의 자원을 관리하는 서비스를 Connection Pool이라고 한다.

간단하게 도식화하자면 아래와 같다.

Connection Poll

  1. Client에서 WAS로 Request 요청을 한다.
  2. Apllication에서는 WAS 내부에서 실행시 미리 보유해놓은 Connection Pool의 인스턴스중 유휴 상태의 Connection이 있는지 조회한다.
    1. 만약 유휴 Connection 이 없다면, 유휴 상태의 Connection이 생길 때 까지 대기한다.
  3. 유휴 상태의 Connection이 있다면, 해당 Connection을 획득한다.
  4. 획득한 Connection을 이용하여 DB에서 쿼리를 조회 한다.
  5. DB에서 쿼리를 통해 조회한 결과를 반환한다.
  6. 사용한 Connection은 다시 재사용을 해야하기 때문에 종료하지 않고, Connection Pool에 반환한다.
  7. Response로 응답을 내보낸다.

HikariCP의 장점?

GITHUB - HikariCP-benchmark

HikariCP의 벤치마크에 대한 정보가 담긴 Github Repository이다.

HikariCP bench

해당 통계를 보면 HikariCP의 성능이 우월하다는 것을 확인 할 수 있다.

또한 HikariCP의 장점을 리스트업 해보자면 아래와 같다.

  1. 교착(Deadlock)상태가 없도록 설계가 되었다.
  2. 자체적으로 Connection leak (커넥션 누수)를 감지 할 수있다.
  3. 모든 구성 속성에 적합한 기본값을 제공
  4. 초경량의 라이브러리 (130kb)
  5. 훌륭한 벤치마크 성능
  6. 빠른 버그 Fix

HikariCP 에서 Connection Pool

그렇다면 HikariCP에서는 어떻게 Connection Pool을 관리할까?

HikariCP는 아래의 Url에서 확인 할 수있듯이, ConcurrentBag 구조체를 이용하여 풀을 관리하고 있으며, 아래의 링크에서 자세한 내용은 확인 할 수 있다.

HikariCP 성능의 비밀

ConcurrentBag 구조체는 C#, .NET ConcurrentBag 클래스에서 차용하고 내부구현을 달리해 HikariCP에 적용했다고 한다.

특징으로는

  • 교착상태가 없는 디자인 (A lock-free design)
  • ThreadLocal 캐싱 (ThreadLocal caching)
  • 큐 훔치기 (Queue-stealing)
  • 직접적인 hand-off 최적화 (Direct hand-off optimizations)

가 있다고 한다..만 어려운말 들이라 참고만 하도록 하겠다.

Connection 획득

HikariCP에서 Connection Pool을 가져오는 부분은 HikariPool.java 의 getConnection()에서 확인할 수 있다.

아래의 소스는 Connection을 가져오는 전문 소스이다.

HikariPool.java_getConnection()

이중에서 눈여겨볼 부분은 아래 소스 중 PoolEntry poolEntry = connectionBag.borrow(timeout, MILLISECONDS); 해당 부분을 살펴보면 된다.

PoolEntry poolEntry = connectionBag.borrow(timeout, MILLISECONDS);
if (poolEntry == null) {
   break; // We timed out... break and throw exception
}

connectionBag.borrow(timeout, MILLISECONDS) 부분의 메소드에서 유휴 상태의 커넥션을 가져온다.

ConcurrentBag.java_borrow()

해당 메소드에서 Connection을 가져오는 방법을 확인해 볼 수 있다.

간략하게 로직을 설명해 보자면,

  1. 현재 실행중인 Thread에서 사용한 이력이 있는 Connection 정보가 있는지 조회하고, 해당 정보가 있을 경우 유휴 상태(STATE_NOT_IN_USE)의 Connection을 가져와 return 한다.
  2. 1.에서 Connetion을 획득하지 못했을 경우 전체 Connection Pool에서 유휴 상태(STATE_NOT_IN_USE)의 Connection이 있는지 조회하고 Connection을 return한다.
  3. 2.에서도 Connection을 획득하지 못했을 경우 handoffQueue에서 기존 HikariCP의 설정중 connection-timeout의 정보(기본값 30000ms)를 가져와 해당 설정의 시간만큼 대기한다.
  4. 최종적으로 3.에서도 획득하지 못할경우 null을 반환하고, SQLException를 발생시킨다.

Connection 반납

Connection 반환 방법 중 HikariCP의 로직을 설명해보도록 하자.

Connection을 가진 Application이 DB와의 연결이 끝난경우 ProxyConnection.java close() => PoolEntry.java의 recycle() => HikariPool.java의 recycle()의 순서로 HikariCP의 Connection 반환 로직을 실행한다.

   /**
 * Recycle PoolEntry (add back to the pool)
 *
 * @param poolEntry the PoolEntry to recycle
 */
@Override
   void recycle(final PoolEntry poolEntry)
   {
       metricsTracker.recordConnectionUsage(poolEntry);

       connectionBag.requite(poolEntry);
   }

Connection을 획득할 때와 마찬가지로 connectionBag 객체를 호출하지만 이번엔 connectionBag.requite(poolEntry); 을 호출하여 Connection을 반환한다.

ConcurrentBag.java_requite()

해당 메소드를 통해 Connection을 반환하는 로직을 확인 할 수 있다.

해당 로직도 간략하게 확인 해보도록 하자.

  1. 반환한 Connection을 유휴 상태(STATE_NOT_IN_USE)로 만든다
  2. 만약 handoffQueue에서 대기중인 Thread가 있다면, 해당 Connection을 제공한다.
  3. Thread의 사용 이력에 추가한다.

이렇게 확인 해볼 수 있다.

정리

이를 통해 HikariCP는 기존에 사용한 적 있는 Connection을 우선적으로 넘기고, 빠르게 재활용 및 다른 대기중인 Thread에게 Connection을 전달 할 수 있도록 구성되었다는 것을 알 수 있다.

다음번에는 HikariCP의 설정 및 설정값에 대해 알아보도록 하자.