데이터베이스 커넥션 풀이 궁금하게 된 이유
서버를 띄우고 API를 제공하면서 WAS를 통해 사용자의 요청을 처리하게 됩니다
간단한 구조로 Nginx - ApacheTomcat(WAS) - DB의 구조를 생각할 수 있으며 성능을 고민해 볼 때
웹 서버 레이어에 대한 성능 테스트, DB 레이어에 대한 성능 테스트로 나누어 판단해 볼 수 있을 것 같습니다

만약 DB에서 병목지점이 발생하여 응답이 느린 것인데, 웹 서버 단, 아키텍처 레이어를 개선한다고 성능 개선이 이루어지지 않겠죠?
이전에 Nginx와 Tomcat을 공부하면서 프로세스 기반 동작 방식과 이벤트 기반 동작 방식의 차이를 이해해 볼 수 있었고,
특히 프로세스 기반의 커넥션을 새로 생성하는 비용이 매우 비싸다는 것을 느낄 수 있었는데, 데이터베이스는 어떻게 커넥션을 관리하는지 궁금해 공부하게 되었습니다
데이터베이스 커넥션 풀이란?
많은 곳에서 다음과 같이 정리하고 있습니다
WAS가 실행되면서 DB와 미리 연결을 해놓은 객체(Connection)들을 pool에 저장해 두었다가 클라이언트의 요청이 들어오면 pool에서 connection을 빌려주고, 처리가 끝나면 다시 Connection을 반납받아 pool에 저장하는 방식
개념적인 정의는 위와 같으며 동작 원리도 아래의 사진으로 정리가 되는 것 같습니다

장점
- 매 연결마다 Connection 객체를 생성하고 소멸시키는 비용을 줄일 수 있다
- 미리 생성된 Connection 객체를 사용하기 때문에, DB 접근 시간이 단축된다
- DB에 접근하는 Connection의 수를 제한하여, 메모리와 DB에 걸리는 부하를 조정할 수 있다
그렇다면 자바는 JDBC를 사용해서 DB 커넥션을 이루는데 스프링부트에서도 많이 사용되는 Hikari Connection Pool 라이브러리를 예로 데이터베이스 커넥션을 어떻게 생성하고 풀은 어떻게 관리되는지에 대해서 정리해 보겠습니다
JDBC의 관한 개념, 실행 과정, 처리 등은 이번 내용에서 조금 벗어난다고 생각하지만 필요한 개념이라고 생각해서 블로그를 참고하면 좋을 것 같습니다
https://mundol-colynn.tistory.com/46
[JDBC] JDBC란? / JDBC 사용객체 / JDBC 처리, 동작순서
JDBC (Java Database Connectivity) -Java가 제공하는, Java프로그램 내에서 Database로 접근할 수 있도록 해주는 표준 인터페이스( 프로그래밍 API) ---> 자바프로그램 내에서 SQL문을 실행하기 위한 자바 API - 자
mundol-colynn.tistory.com
Hikari Connection Pool의 동작 방식

- Thread가 Connection을 요청
- Connection Pool의 각자의 방식에 따라 유휴 Connection을 찾아서 반환
- Hikari CP의 경우, 이전에 사용했던 Connection이 존재하는지 확인하고, 이를 우선적으로 반환하는 특징이 있다
그렇다면 이전 사용했던 Connection 목록 중 사용 가능한 Connection이 존재하는지 확인한다는 것은 사용 가능한 Connection이 존재하지 않을 수 있다는 의미이다!

가능한 Connection이 존재하지 않으면
- HandOffQueue를 Polling 하면서 다른 Thread가 Connection을 반납하기를 기다린다
언제까지 기다릴까?
- 지정한 TimeOut 시간까지 대기하다가 시간이 만료되면 예외를 던짐
ConnectionTimeOut은 어느 정도로?
https://stackoverflow.com/a/2748741
In summary:
ConnectionTimeout=0
is bad, make it something reasonable like 30 seconds.
ConnectionLifetime=0 // 0은 커넥션을 재생성하지 않겠다는 의미
is okay
ConnectionPooling=disabled
is bad, you will likely want to use it.
- ConnectionTimeout=0 은 좋지 않다. 합리적인 초단위(30초)로 명시해 주는 게 좋다.
- ConnectionLifetime=0 은 좋다 -> 커넥션을 재생성하지 않겠다
- ConnectionPooling=disabled 은 좋지 않다. pool을 사용으로 활성화해 주는 게 좋다.
ConnectionTimeout은 스레드가 커넥션을 요청하고 대기하는 시간을 의미하는 것 같다
그렇다면 커넥션을 얻은 사용자가 반납을 안 한다면? 언제까지 커넥션을 유지시킬지?
찾아보니 이를 잘 설명하고 있는 글을 D2 블로그에서 찾았다

TransactionTimeout
- TransactionTimeout은 프레임워크(Spring, EJB Container)나 애플리케이션 레벨에서 유효한 타임아웃
- `StatementTimeout x N(Statement 수행 수) + α(가비지 컬렉션 및 기타)`의 수행 시간을 허용할 수 있는 최대 시간 이내로 제한하려 할 때 사용
- Spring의 @Transactional을 이용하여 타임아웃을 설정하는 게 가장 익숙한 예
- Spring에는 Transaction Synchronization방식이라고 하여 Connection을 ThreadLocal에 저장해 두고 사용한다. ThreadLocal에 Connection 저장 시 Transaction의 시작 시간과 타임아웃 시간을 같이 기록하고, Proxy Connection을 사용하여 Statement를 생성하는 작업을 시도할 경우 경과 시간을 체크하여 예외를 발생시키도록 구현
StatementTimeout
- Statement 하나가 얼마나 오래 수행되어도 괜찮은지에 대한 한계 값
- JDBC API인 java.sql.Statement.setQueryTimeout(int timeout) 메서드로 설정
- StatementTimeout 시간은 애플리케이션 특성에 따라 지정하기 때문에 이에 대한 설정 권장 값은 없다
JDBC SocketTimeout
- JDBC 드라이버의 SocketTimeout 값은 DBMS가 비정상으로 종료되었거나 네트워크 장애(기기 장애 등)가 발생했을 때 필요한 값
- TCP/IP의 구조상 소켓에는 네트워크의 장애를 감지할 수 있는 방법이 없다. 그렇기 때문에 애플리케이션은 DBMS와의 연결 끊김을 알 수 없는데, SocketTimeout이 설정되어 있지 않다면 무한정 대기할 수 있다(이러한 Connection을 Dead Connection이라 부르기도 한다)
- SocketTimeout 값을 Statement의 수행 시간제한을 위해 사용하는 것은 바람직하지 않다. 그러므로 SocketTimeout 값은 StatementTimeout 값보다는 크게 설정해야 한다
OS SocketTimeout
- SocketTimeout이나 ConnectTimeout을 설정하지 않으면 네트워크 장애가 발생해도 애플리케이션이 대부분 이를 감지할 수 없다
- 연결이 되거나 데이터를 읽을 수 있을 때까지 애플리케이션이 무한정 기다리게 됨
- 서비스에서 발생한 실재 장애 상황에서는 30분 후에 애플리케이션(WAS)이 재연결을 시도하여 문제가 해결되는 경우가 많다
- OS에서도 SocketTimeout 시간을 설정할 수 있기 때문
- OS의 SocketTimeout에 의해 문제가 해결된 것
- OS는 일정 시간 동안 패킷 재전송을 시도하다고 한계 값에 도달하면 에러를 발생시킴
위와 같이 Timeout을 통해 커넥션을 사용할 때 사용 시간제한, 유휴 커넥션을 기다리는 대기 시간을 조정할 수 있으며 Dead Connection에 대한 대응, 감지 분석에 올바른 Timeout설정을 통해 장애에 대응할 수 있는 기반이 될 것 같다
다시 돌아와서 Hiraki CP에서 Connection 반납과 할당을 살펴보자

- 사용한 Connection을 반납하면 Connection Pool이 Connection 사용 내역을 기록
- HandOffQueue에 반납된 Connection을 삽입한다.
- 이를 통해 HandOffQueue를 Polling 하던 Thread는 Connection을 획득하고 작업을 이어나간다
- Timeout을 통해 무한정 polling하지는 않을 거라는 게 이해가 됐을 것이다
커넥션 풀 유의사항
동시 접속자가 많을 경우?
많은 DB 접근이 발생할 경우에는 커넥션은 한정되어 있기 때문에 쓸 수 있는 커넥션이 반납될 때까지 기다려야 한다
커넥션을 생성할 시에는 커넥션 또한 객체이므로 메모리를 차지하게 되고, 프로그램의 성능을 떨어뜨리는 원인이 된다
즉, WAS에서 커넥션 풀을 크게 설정하면 메모리 소모가 큰 대신 많은 사용자가 대기 시간이 줄어들고, 반대로 커넥션 풀을 작게 설정하면 그만큼 대기 시간이 길어진다. 따라서 사용량에 따라 적정량의 커넥션 객체를 생성해 두어야 한다 (트레이드 오프)
Connection Pool의 크기는 얼마나 적절할까?

- Connection을 사용하는 주체인 Thread의 개수보다 커넥션 풀의 크기가 크다면 사용되지 않고 남는 커넥션이 생겨 메모리의 낭비가 발생하게 된다
- MySQL은 최대 연결 수를 무제한으로 설정한 뒤 부하 테스트를 진행하면서 최적화된 값을 찾는 것을 추천
- Hikari CP의 공식 문서에 의하면, 1 connections = ((core_count) * 2) + effective_spindle_count)로 정의하고 있다
- core_count는 현재 사용하는 서버 환경에서의 CPU 개수를 의미
- core_count * 2를 하는 이유는 Context Switching 및 Disk I/O와 관련이 있음
- Context Switching으로 인한 오버헤드를 고려하더라도 데이터베이스에서 Disk I/O(혹은 DRAM이 처리하는 속도)보다 CPU 속도가 월등히 빠르다
- Thread가 Disk와 같은 작업에서 블로킹되는 시간에 다른 Thread의 작업을 처리할 수 있는 여유가 생기고, 여유 정도에 따라 멀티 스레드 작업을 수행할 수 있게 된다. Hikari CP가 제시한 공식에서는 계수를 2로 선정하여 Thread 개수를 지정하였다
- core_count * 2를 하는 이유는 Context Switching 및 Disk I/O와 관련이 있음
- effective_spindle_count는 기본적으로 DB 서버가 관리할 수 있는 동시 I/O 요청 수이다.
- 하드 디스크 하나는 spindle 하나를 갖는다
- 디스크가 16개 있는 경우, 시스템은 동시에 16개의 I/O 요청을 처리할 수 있다
정리
- 커넥션 풀이란 WAS가 실행되면서 DB와 미리 연결을 해놓은 객체(Connection)들을 pool에 저장해 두었다가 클라이언트의 요청이 들어오면 pool에서 connection을 빌려주고, 처리가 끝나면 다시 Connection을 반납받아 pool에 저장하는 방식
- 장점은 불필요한 과정(Connection객체를 생성, 삭제)을 줄여서 성능을 높일 수 있다.
- WAS에서 커넥션 풀을 크게 설정하면 메모리 소모가 큰 대신 많은 사용자가 대기 시간이 줄어들고, 반대로 커넥션 풀을 적게 설정하면 그만큼 대기 시간이 길어진다. 따라서 사용량에 따라 적정량의 커넥션(Connection) 객체를 생성해두어야 한다
- MySQL, PostgreSQL, Hikari CP 등의 공식 문서를 찾아보면 적정한 커넥션 풀의 크기를 테스트하고 정리한 내용들을 찾아볼 수 있으며 직접 부하테스트를 해보며 적정 값을 찾아볼 수 있다
- 커넥션을 관리함에 있어 Timeout을 설정할 수 있는데 Transaction, Statement, JDBC Socket, 커넥션 Timeout을 설정하여 커넥션의 사용 시간, 대기 시간을 지정하여 장애에 대응하거나 장애를 파악할 수 있으며 일반적으로 사용하기 좋은 값으로 정해진 건 없다
Reference
https://code-lab1.tistory.com/209
DB 커넥션 풀(Connection pool)이란? HikariCP란?
커넥션 비용 WAS(Web Application Server)와 데이터베이스 사이의 연결에는 많은 비용이 든다. MySQL 8.0을 기준으로 INSERT 문을 수행할 때 필요한 비용의 비율은 다음과 같다. 괄호 안의 숫자가 비율을 의
code-lab1.tistory.com
https://steady-coding.tistory.com/564
[데이터베이스] Connection Pool이란?
cs-study에서 스터디를 진행하고 있습니다. DB Connection DB를 사용하기 위해 DB와 애플리케이션 간 통신을 할 수 있는 수단 DB Connection은 Database Driver와 Database 연결 정보를 담은 URL이 필요함 Java의 DB Con
steady-coding.tistory.com
https://kakaocommerce.tistory.com/45
JDBC 커넥션 풀들의 리소스 관리 방식 이해하기
이미 JDBC 커넥션 풀 관련 다양한 글들이 존재하지만 대부분 오래된 commons-dbcp 1.x 의 내용 위주라서 최근에 많이 사용되고 있는 tomcat-jdbc-pool과 hikariCP에 대한 내용을 추가로 조사하고 정리해 보았
kakaocommerce.tistory.com
https://d2.naver.com/helloworld/1321
'데이터베이스' 카테고리의 다른 글
데이터베이스 반정규화 (1) | 2023.09.06 |
---|---|
데이터베이스 정규화 (0) | 2023.09.06 |
MySQL 인덱스 동작 확인하기 (0) | 2023.07.23 |
조회 성능 최적화를 위한 인덱스 (2) (1) | 2023.07.06 |
조회 성능 최적화를 위한 인덱스 (1) (0) | 2023.07.05 |