개요
- 지금까지 Hikari DataSource를 사용하면서, HikariCP에서 제공하는 maximumPoolSize, connectionTimeout, maxLifetime, idleTimeout 과 같은 설정값 정도만 알고 있었지 내부적으로 HikariCP가 어떤식으로 Connection Pool을 관리하는지 알지 못했다.
- 또한, 언젠가 Connection Pool에서 장애가 발생할 수 있을 것을 대비해서 이번 기회에 HikariCP 코드를 분석해 보려고 한다.
Connection Pool이란?
- 커넥션 풀이란 데이터베이스와 연결된 커넥션을 미리 만들어 놓은 Pool을 의미한다.
- 만약 커넥션 풀이 없다면?
- 위와 같이 WAS 내부에 DB 드라이버와 DB가 직접 통신하며 Connection을 가져 와야 된다.
- 하지만 Connection을 맺는 작업은 상당히 무거운 작업이다.
- 그 이유는 DB 드라이버와 DB 간에 TCP/IP 통신을 맺어야 하고, 그 과정에서 3 way handshake 와 같은 네트워크 동작들이 수반되기 때문이다.
- 그 뿐만 아니라 username, password를 바탕으로 인증 처리도 해야되고, 인증이 끝난 후 DB는 별도의 세션도 만들어야한다.
- 위의 과정들이 다 끝나야 비로소 DB 드라이버는 Connection객체를 만들어 전달할 수 있다.
- 만약 커넥션 풀이 없고 매번 위의 과정대로 Connection을 직접 만든다면, WAS, DB 모두 부하가 걸리고 이는 장애로 이어질 것이다.
- 커넥션 풀이 있다면?
- 위와 같이 DB에서 미리 커넥션을 맺어 Connection Pool에 담아두고 Connection을 DB로 반환하지 않고 재활용해서 사용한다.
- 따라서 커넥션이 필요한 시점에 DB로 부터 Connection을 가져오는 것이 아니라 Connection Pool로부터 Conneciton을 받아온다.
- 이렇게 되면, Connection을 새로 만드는 비용을 절약할 수 있을 뿐 아니라 DB에도 부하를 줄일 수 있기 때문에 성능적으로 많은 이득을 얻을 수 있다.
Connection Pool에 대한 간단히 개념을 정리 했으니 이제부터 본격적으로 HikariCP가 무엇인지 정리해 보자
HikariCP란?
- Spring에서 DataSource는 Connection pool을 관리하고 연동할 수 있게 하는 표준 인터페이스다.
- HikariCP는 SpringBoot2.0부터 DataSource의 Default 구현체로 채택되었다.
- hikariCP는 다른 구현체보다 더 높은 성능을 가지고 있다.
HikariCP 옵션
- connectionTimeout
- 클라이언트가 pool에 connection을 요청하는데 기다리는 최대시간을 말한다.
- default 는
30000(30초)
이다. - 설정한 시간을 초과하면 SQLException이 발생한다.
- 최소 연결 시간은 250ms 이다.
- maximunPoolSize
- 유휴 및 사용중인 connection을 포함해서 pool에 보관할 수 있는 최대 커넥션 수를 말한다.
- default 는
10
이다. - 사용할 수 있는 커넥션이 없다면, connection Timeout 시간 만큼 대기한다. -> 시간을 초과한다면 SQLException 이 발생한다.
- minimumIdle
- pool 에서 유지가능한 최소 커넥션 갯수를 말한다.
- default 는 maximumPoolSize 와 동일하다.
특별한 이유가 없다면 설정하지 않는 것이 성능적인 측면에서 좋다.
- idleTimeout
- pool에 유휴 상태로 유지시킬 수 있는 최대 시간을 말한다.
- default는
600000(10분)
이다. - minimumIdle이 maximumPoolSize보다 작은 경우에만 사용 가능하다.
- pool에 있는 connection 수가 minimumIdle에 도달했을 때, 이후에 반환되는 connection에 대해서 바로 반환되지 않고, idleTimeout 만큼 유휴 상태로 있다가 폐기된다.
- maxLifeTime
- connection의 최대 유지 시간을 말한다.
- default는
1800000(30분)
이다. - connection이 maxLifeTime 만큼 지났을 때, 사용중인 connection은 바로 폐기되지 않고, 작업이 완료되면 폐기된다. -> 하지만 유휴 커넥션은 바로 폐기된다.
- readOnly
- pool에서 얻은 connection이 기본적으로 readOnly로 설정될지를 결정한다.
- default는 false 이다.
- 물론 데이터베이스가 readOnly 속성을 지원할 경우에만 사용할 수 있다.
- connectionTestQuery
- 데이터베이스 연결이 여전히 활성화 되어있는지 확인하기 위한 쿼리이다.
- JDBC4 드라이버를 지원한다면 이 옵션은 설정하지 않는 것을 추천한다.
- poolName
- Connection Pool의 이름을 지정할 수 있다.
- driverClassName
- HikariCP는 기본적으로 jdbcUrl을 참조하여 자동으로 driver를 설정하려고 시도하지만, 몇몇 오래된 driver들은 driverClassName를 표시해 줘야 하는 경우가 있다.
- validationTimeout
- 커넥션이 유효한지 검사할 때 사용되는 timeout이다.
- default는 5000(5초)이다.
- 최소값은 250ms이다.
- leakDetectionThreshold
- 커넥션이 누수 로그메시지가 나오기 전에 커넥션을 검사하여 pool에서 커넥션을 내보낼 수 있는 시간이다.
- 0으로 설정하면 사용하지 않는 것으로 간주한다.
- 최솟값은 2000이다.
HikariCP 의 Connection Pool 동작 과정
- 뒤에서 좀더 자세하게 설명 하겠지만 간단히 동작과정을 정리해 보면 아래와 같다.
Connection Pool 구성 요소
- 먼저 Connection Pool의 구성요소를 간단하게 정리해 보면 아래와같이 3가지로 나뉜다.
- threadList: Thread가 사용한 Connection의 이력을 남긴다. -> 추후에 캐시와 비슷한 역할로 사용된다.
- 사용한 이력이 있으면 사용했던 커넥션을 빠르게 반환해 준다. (물론 해당 커넥션 상태가 idle인 경우에만 해당된다.)
- sharedList: 실제로 Connection을 가지고 있는 공간이다.
- handOffQueue: sharedList에 사용할 Connection이 없어서 Thread가 Connection을 기다릴 때 handOffQueue 앞에서 대기한다.
- 다른 Thread에서 Connection을 반환할 때, handOffQueue 앞에서 대기하고 있는 Thread가 있다면 handOffQueue로 Connection을 반환해서 대기하고 있는 Thread가 좀 더 빠르게 Connection을 획득할 수 있다.
Connection 획득 과정
- 1번 과정: 위와 같이 Thread가 Connection을 요청한다면 HikariCP는 먼저 ThreadList를 검사해서 해당 Thread가 사용했던 Connection 이력이 있다면, 해당 Connection을 먼저 전달해준다. (Connection 상태가 idle인 경우만 해당된다.)
- 2번 과정: 만약 ThreadList에서 Connection을 반환해주지 못했다면, SharedList에서 줄 수 있는 Connection이 있는지 검사하고 있으면 전달해준다.
- 3번 과정: 만약 SharedList에도 Connection을 반환해주지 못했다면, handOffQueue앞에서 대기한다. -> connectionTimeout 시간 내에 다른 Thread에서 Connection을 반환해주면 handOffQueue에서 connection을 반환 받고, connectionTimeout 시간이 지나도록 Connection을 반환받지 못했다면 예외를 던진다.
Connection 획득 과정 예시 #1
가정: Thread1이 C1를 사용한 이력이 있고, 현재 Thread2가 C1, C3를 점유하고 있다. 또한, 여기서 Thread1이 Connection을 요청한다.
- 먼저 ThreadList를 검사한다. -> Thread1이 c1을 사용한 이력이 있지만 Thread2가 c1을 사용중이기 때문에 패스한다.
- 그 다음 SharedList를 검사한다. -> c2가 사용가능 함으로 c2를 반환한다.
Connection 획득 과정 예시 #2
가정: Thread1이 C1를 사용한 이력이 있고, 현재 Thread2가 C1, C2, C3를 점유하고 있다. 또한, 여기서 Thread1이 Connection을 요청한다.
- 먼저 ThreadList를 검사한다. -> Thread1이 c1을 사용한 이력이 있지만 Thread2가 c1을 사용중이기 때문에 패스한다.
- 그 다음 SharedList를 검사한다. -> SharedList에도 사용가능한 Connection이 없으니 패스한다.
- 마지막으로 Thread1이 handOffQueue에서 Thread2가 반환한 c2를 반환 받는다.
Connection 반환 과정
가정: Thread1이 Connection을 반환 하려고 한다. 또한, 현재 handOffQueue앞에 대기하고 있는 Thread는 없다.
- 먼저 Connection의 상태를 active -> idle로 바꾼다.
- handOffQueue 앞에 대기하고 있는 Thread가 없기 때문에 ThreadList에 이력을 남기고 반환한다.
가정: Thread1이 Connection을 반환 하려고 한다. 또한, 현재 Thread2가 handOffQueue앞에 대기하고 있다.
- 먼저 Connection의 상태를 active -> idle로 바꾼다.
- handOffQueue 앞에서 Thread2가 대기하고 있기때문에 Connection을 handOffQueue로 반환하고, 대기하고 있던 Thread2가 Connection을 반환받는다.
이제까지 간단한 개념들을 정리해 보았으니, 다음편부터 본격적으로 HikariCP 내부 코드를 까보도록 하자
References
'Jdbc' 카테고리의 다른 글
HikariCP 코드 분석하기 3편(HikariCP 커넥션 풀 초기화 과정 디버깅) (0) | 2023.01.21 |
---|---|
HikariCP 코드 분석하기 2편 (HikariCP 커넥션 풀 초기화 과정) (0) | 2023.01.21 |
JOOQ (Java Object Oriented Querying) (0) | 2022.03.24 |
Spring JDBC (0) | 2022.02.27 |
JPA vs MyBatis (0) | 2022.02.27 |