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 연결은 드라이버를 로드하고 연결하여 객체를 받아와야 하는 과정을 가지고 있다.
이 과정은 매번 사용자가 요청할 때마다 드라이버를 로드하고 커넥션 객체를 생성하여 연결하고 종료하는 과정이 불편하고 속도와 자원 소모에 대한 단점이 있다.
→ 이를 보완하기 위해 데이터베이스 커넥션 풀을 사용!
(아래와 같이 컨넥션 풀이 없다면 사용자 요청이 있을때마다 드라이버를 로드함.
데이터베이스 커넥션 풀의 과정
- WAS가 실행 되면서 Pool 내에 Connection들을 생성
- HTTP의 요청이 올 때, Pool 내에서 Connection 객체를 가져다가 사용
- 사용이 완료된 Connection 객체는 Pool 내에 반환
(내생각: 아직은 잘 모르겠지만 맨 처음 서버를 시작하면 waiting시간이 오래 걸리는 이유중의 하나가 이런과정 때문이 아닌가 싶다. WAS가 실행되기 시작하면 Pool안에 Connection객체들을 미리 만들어 놓는 것이다)
🍊 스프링에서의 커넥션 풀
자바에서는 기본적으로 DataSource 인터페이스를 사용하여 커넥션풀을 관리한다.
Spring에서는 사용자가 직접 커넥션을 관리할 필요없이 자동화된 기법들을 제공하는데
SpringBoot 2.0 이전에는 tomcat-jdbc를 사용하다,
현재 2.0이후 부터는 HikariCP를 기본옵션으로 채택 하고있다.
데이터베이스 커넥션 풀의 장점
- WAS와 데이터베이스와의 연결을 정해놓은 만큼만 미리하여, 매번 생성하는 비용을 줄일 수 있다.
- 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
'Spring&IntelliJ' 카테고리의 다른 글
CQS(Command Query Separation)에 대해서 (0) | 2023.12.28 |
---|---|
@PersistenceContext와 EntityManager, 그리고 영속성(Persistence) (0) | 2023.12.28 |
Spring과 MySQL 연동에서의 application.properties설정 (0) | 2023.12.28 |
Logback로깅 프레임워크 (1) | 2023.12.28 |
배포환경과 Spring Boot Profile적용 (1) | 2023.12.28 |