본문 바로가기

Spring&IntelliJ

DB 컨넥션 풀과 Hikari Connection pool 에 대해서

 

출처: https://velog.io/@miot2j/Spring-DB%EC%BB%A4%EB%84%A5%EC%85%98%ED%92%80%EA%B3%BC-Hikari-CP-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0



https://velog.io/@dongvelop/Spring-Boot-Hikari-CP-%EC%BB%A4%EC%8A%A4%ED%85%80%EC%9C%BC%EB%A1%9C-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94%ED%95%98%EA%B8%B0

실무환경에서 DataBase 성능 최적화 및 ORM에 대해 이야기 할때 커넥션풀이라는 단어가 자주 등장하여 한번 정리가 필요할 것 같아 정리한 포스트 입니다.

🍌 JDBC란?

Hikari CP(히카리 커넥션풀)을 알아보기에 앞서 JDBC의 개념을 정리하자면,
JDBC는 Java Database Connectivity의 약자로 자바에서 데이터베이스에 접속할 수 있도록 하는 자바 API다.

JDBC는 데이터베이스에서 자료를 쿼리하거나 업데이트하는 방법을 제공한다.

 

🍑 DB 커넥션 풀이란

일반적인 데이터 연동과정은 웹 어플리케이션이 필요할 때마다 데이터베이스에 연결하여 작업하는 방식입니다.
하지만 이런 식으로 필요할 때마다 연동해서 작업할 경우 데이터베이스 연결에 시간이 많이 걸리는 문제가 발생합니다.

예를들어 거래소의 경우, 동시에 몇천명이 동시에 거래 및 조회 기능을 사용하는데 매번 데이터베이스와 커넥션을 맺고 푸는 작업을 한다면 굉장히 비효율적일 것입니다.

이 문제를 해결하기 위해 현재는 웹 어플리케이션이 실행됨과 동시에 연동할 데이터베이스와의 연결을 미리 설정해 둡니다.

그리고 필요할 때마다 미리 연결해 놓은 상태를 이용해 빠르게 데이터베이스와 연동하여 작업을 합니다.

이렇게 미리 데이터베이스와 연결시킨 상태를 유지하는 기술을 커넥션 풀
(Connection Pool, CP)라고 합니다.

 

데이터베이스 커넥션 풀을 사용하는 이유

JDBC 연결은 드라이버를 로드하고 연결하여 객체를 받아와야 하는 과정을 가지고 있다.

이 과정은 매번 사용자가 요청할 때마다 드라이버를 로드하고 커넥션 객체를 생성하여 연결하고 종료하는 과정이 불편하고 속도와 자원 소모에 대한 단점이 있다.

→ 이를 보완하기 위해 데이터베이스 커넥션 풀을 사용!

(아래와 같이 컨넥션 풀이 없다면 사용자 요청이 있을때마다 드라이버를 로드함.

데이터베이스 커넥션 풀의 과정

  1. WAS가 실행 되면서 Pool 내에 Connection들을 생성
  2. HTTP의 요청이 올 때, Pool 내에서 Connection 객체를 가져다가 사용
  3. 사용이 완료된 Connection 객체는 Pool 내에 반환

 

(내생각: 아직은 잘 모르겠지만 맨 처음 서버를 시작하면 waiting시간이 오래 걸리는 이유중의 하나가 이런과정 때문이 아닌가 싶다. WAS가 실행되기 시작하면 Pool안에 Connection객체들을 미리 만들어 놓는 것이다)

 

🍊 스프링에서의 커넥션 풀

자바에서는 기본적으로 DataSource 인터페이스를 사용하여 커넥션풀을 관리한다.

Spring에서는 사용자가 직접 커넥션을 관리할 필요없이 자동화된 기법들을 제공하는데

SpringBoot 2.0 이전에는 tomcat-jdbc를 사용하다,
현재 2.0이후 부터는 HikariCP를 기본옵션으로 채택 하고있다.

데이터베이스 커넥션 풀의 장점

  1. WAS와 데이터베이스와의 연결을 정해놓은 만큼만 미리하여, 매번 생성하는 비용을 줄일 수 있다.
  2. Connection에 대해 조정을 할 수 있다.
    • 커넥션 풀을 크게 설정하면? → 메모리 소모가 큰 대신 많은 사람의 대기시간이 줄어 듬
    • 커넥션 풀을 작게 설정하면? → 사용자의 대기시간이 길어짐

 

🍓 왜 Hikari Cp일까?

히카리 벤치마킹 페이지를 참고하면 아래와 같이 월등한 성능을 보인다는 것을 알 수있다.


HikariCp가 다른 커넥션풀 관리 프레임워크보다 빠른 성능을 보여주는 이유는
커넥션풀의 관리 방법에 있다.

히카리는 Connection 객체를 한번 Wrappring한 PoolEntry로 Connection을 관리하며,
이를 관리하는 ConcurrentBag이라는 구조체를 사용하고 있다.

ConcurrentBag은 HikariPool.getConnection() -> ConcurrentBag.borrow()라는 메서드를 통해 사용 가능한(idle) Connection을 리턴하도록 되어있다.

이 과정에서 커넥션생성을 요청한 스레드의 정보를 저장해두고 다음에 접근시 저장된 정보를 이용해 빠르게 반환을 해준다.

이러한 방법 때문에 속도에 이점이 있으며 해당 방법의 자세한 설명은 아래 블로그를 참조하면 좋을 것 같다.
HikariCP Dead lock에서 벗어나기 (이론편)

 

🍎 Hikari CP 사용법

build.gradle에 따로 추가할 필요 없이
"org.springframework.boot:spring-boot-starter-jdbc"
를 추가하면 자동으로 추가된다( 왜? 위에서 설명한데로 현재 Hikari는 스프링부트 2.0이후 기본 옵션으로 설정되어있는 DB connection pool이기 때문이다).

이후 application.yml에 설정값을 추가하면 되는데

spring:
 datasource:
   url: jdbc:mysql://localhost:3306/world?serverTimeZone=UTC&CharacterEncoding=UTF-8
   username: root
   password: your_password
   hikari:
     maximum-pool-size: 10
     connection-timeout: 5000
     connection-init-sql: SELECT 1
     validation-timeout: 2000
     minimum-idle: 10
     idle-timeout: 600000
     max-lifetime: 1800000

server:
 port: 8000

options

  • maximum-pool-size: 최대 pool size (defailt 10)
  • connection-timeout: (말 그대로)
  • connection-init-sql: SELECT 1
  • validation-timeout: 2000
  • minimum-idle: 연결 풀에서 HikariCP가 유지 관리하는 최소 유휴 연결 수
  • idle-timeout: 연결을위한 최대 유휴 시간
  • max-lifetime: 닫힌 후 pool 에있는 connection의 최대 수명 (ms)입니다.
  • auto-commit: auto commit 여부 (default true)

 

🍋 DeadLock 피하기

이론적으로 필요한 최소한의 커넥션 풀 사이즈를 알아보면 다음과 같다.

PoolSize = Tn × ( Cm -1 ) + 1

  • Tn : 전체 Thread 갯수
  • Cm : 하나의 Task에서 동시에 필요한 Connection 수

위와 같은 식으로 설정을 한다면 데드락을 피할 수는 있겠지만 여유 커넥션풀이 하나 뿐이라 성능상 좋지 못하다.
따라서 커넥션풀의 여유를 주기위해 아래와 같은 식을 사용하는것을 권장한다.

PoolSize = Tn × ( Cm - 1 ) + ( Tn / 2 )

  • thread count : 16
  • simultaneous connection count : 2
  • pool size : 16 * ( 2 – 1 ) + (16 / 2) = 24