일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 알고리즘
- react
- IT
- redis
- MSA
- 자료구조
- design pattern
- Java
- spring webflux
- JPA
- OS
- MySQL
- 백준
- C
- 컴퓨터구조
- Algorithm
- Galera Cluster
- Proxy
- 디자인 패턴
- mongoDB
- Kafka
- Data Structure
- JavaScript
- c언어
- Heap
- 네트워크
- 운영체제
- 파이썬
- Spring
- 자바
Archives
- Today
- Total
시냅스
HikariCP 101 : 코드로 알아보는 HikariCP 본문
Connection Pool
https://liltdevs.tistory.com/136
Connection pool 은 데이터베이스와의 연결을 효율적으로 관리하기 위한 기술입니다.
미리 생성된 일정 개수의 데이터베이스 커넥션을 재사용하여
데이터베이스에 대한 연결 및 해제에 소요되는 시간과 리소스를 절약하여 전체 성능을 향상시킬 수 있습니다.
Hikari CP
HikariCP 는 SpringBoot 2.0 이후부터 default connection pool 로 채택되었습니다.
가볍고 높은 성능을 제공하는 커넥션 풀 라이브러리 입니다.
HikariCP가 어떻게 다른 커넥션 풀보다 빠른 성능을 지닐 수 있는지 아래에서 알아보겠습니다.
Hikari CP 가 빠른 이유
- HikariCP는 매우 경량화되어 있습니다.
- 코드가 간결하고 단순하며, 작은 라이브러리로 구성되어 있습니다.
- 따라서 클래스 로딩 및 인스턴스화 시간이 매우 짧아 커넥션 풀의 초기화, 확장에 이점을 가집니다.
- 자체적인 자료구조를 사용하기도 합니다. (FastList, ConcurrentBag…)
- 깃허브
- HikariCP는 자체 스레드 풀을 사용하여 커넥션 확장 및 유지 관리를 병렬로 처리할 수 있습니다.
- 동시성을 높이고 처리량을 높일 수 있습니다.
- HikariCP는 커넥션 확장 및 유지 관리에 대한 뛰어난 알고리즘을 사용합니다.
- 커넥션 풀 내의 커넥션 수를 동적으로 조절하고, 유휴 커넥션을 검사하여 유휴 시간이 지난 커넥션을 종료합니다.
- connection 이 부족할 때에는 PoolEntryCreator 를 이용하고,
- connection 이 너무 많을 때에는 HouseKeeper 를 이용합니다.
- HouseKeeping
// ... // idelTimeout 이 정해져잇고, 최소값이 최대 값보다 적은 경우 pool 을 동적으로 조정한다. // scheduleWithFixedDelay 를 통해 스케쥴러로 등록되어 있다. if (idleTimeout > 0L && config.getMinimumIdle() < config.getMaximumPoolSize()) { logPoolState("Before cleanup "); final var notInUse = connectionBag.values(STATE_NOT_IN_USE); var maxToRemove = notInUse.size() - config.getMinimumIdle(); // 사용하지 않는 connection close for (PoolEntry entry : notInUse) { if (maxToRemove > 0 && elapsedMillis(entry.lastAccessed, now) > idleTimeout && connectionBag.reserve(entry)) { closeConnection(entry, "(connection has passed idleTimeout)"); maxToRemove--; } } // ...
- 커넥션 풀 내의 커넥션 수를 동적으로 조절하고, 유휴 커넥션을 검사하여 유휴 시간이 지난 커넥션을 종료합니다.
- HikariCP는 적극적인 커넥션 리사이클링을 사용합니다.
- 커넥션을 반환할 때 커넥션의 모든 상태를 초기화하고 재사용 가능한 상태로 만듭니다.
- 커넥션 생성 및 제거에 따른 오버헤드를 크게 줄일 수 있습니다.
// HikariPool에 connection을 반환하며 recycle 하게 된다. @Override void recycle(final PoolEntry poolEntry) { metricsTracker.recordConnectionUsage(poolEntry); connectionBag.requite(poolEntry); } // ConcurrentBag.java public void requite(final T bagEntry) { // 사용하지 않는 connection 으로 setting bagEntry.setState(STATE_NOT_IN_USE); for (var i = 0; waiters.get() > 0; i++) { // 만약 connection 을 요구하는 thread 가 있다면 우선적으로 connection 을 반환 if (bagEntry.getState() != STATE_NOT_IN_USE || handoffQueue.offer(bagEntry)) { return; } // ... } // 커넥션 정보를 thread local 에 저장한다. final var threadLocalList = threadList.get(); if (threadLocalList.size() < 50) { threadLocalList.add(weakThreadLocals ? new WeakReference<>(bagEntry) : bagEntry); } }
// 차후 connection 을 빌려올 때 thread local 에 있는 connection 을 먼저 시도한다 // thread local 에 connection 이 있다는 것은, 가용한 connection 이 있다는 것으로 판단한다. public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException { // Try the thread-local list first final var list = threadList.get(); for (int i = list.size() - 1; i >= 0; i--) { final var entry = list.remove(i); @SuppressWarnings("unchecked") final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry; if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) { return bagEntry; } } // handoffQueue 를 체크하고, 이후에는 timeout 시간까지 대기한다. }
도식화로 보는 Hikari CP Connection borrow/close
Connection borrow
내부적으로 ConcurrentBag 이라는 구조체를 이용해 connection 관리합니다.
HikariPool.getConnection() → ConcurrentBag.borrow() 를 사용해 가능한 connection 리턴합니다.
- connection.getConnection
- 이전에 사용한 Connection 이 idle 이면 해당 connection 반환
- 이전에 사용한 내역이 없었고, 사용 가능한 connection 이 있다면 해당 connection 반환
- 없다면 handoffQueue 에서 대기
- 30초 동안 대기 후 connection 수립 안되면 timeout에러
Connection close
connection 이 close 되거나 exception 으로 rollback 될 때 실행되는 flow 입니다.
- connection.close → ConcurrentBag.requite()
- 해당 커넥션을 idle connection 으로 상태 변경 (poolEntry.setState(STATE_NOT_IN_USE))
- 만약 큐에서 커넥션을 대기하는 스레드가 있으면 우선 빌려주고
- 커넥션을 빌려주고 커넥션 정보를 threadLocal 에 추가함
- 없다면 해당 커넥션 정보를 threadlocal 에 추가함
참고 : Root Transaction
- Root Transaction 은 Hikari CP 커넥션 풀링 시스템 내부에서 발생하는 트랜잭션입니다.
- 커넥션 획득, 쿼리 실행, 트랜잭션 처리 등의 모든 작업이 내부적으로 Root Transaction 안에서 수행됩니다.
- Transactional 을 위해 proxy 가 실행되는 시점에 root transaction 또한 동시에 실행됩니다.
- 마찬가지로, proxy 가 commit 하거나 rollback 하는 시점에 connection 을 반환하고 동시에 종료됩니다.
- 때문에 트랜잭션 불일치나 다른 문제가 발생하지 않도록 보장합니다.
- 또한 root transaction 내부에서 connection 획득에 실패하거나 문제가 생긴다면 hikariCP 가 우선적으로 처리합니다.
'Java, Spring' 카테고리의 다른 글
Java NIO 의 작동 원리 (0) | 2023.04.04 |
---|---|
코드로 살펴보는 Spring Thread Model 과 blocking/non-blocking I/O (0) | 2023.04.02 |
Java 로 구현하는 In-Memory Cache (2) | 2023.03.26 |
Java 참조 유형 과 GC (strong, soft, weak, phantom reference) (0) | 2023.03.26 |
Java8 Parallel Stream 과 성능, 동시성 문제에 대해 (0) | 2023.03.25 |
Comments