HTTP 200 OK

Memento mori & Carpe diem

CS

Connection pool

sjoongh 2022. 12. 6. 16:39

커넥션 풀(DBCP)란?

  • 웹 컨테이너(WAS)가 실행되면서 DB와 미리 connection(연결)을 해놓은 객체들을 pool에 저장해두었다가, 클라이언트 요청이 오면 connection을 빌려주고, 처리가 끝나면 다시 connection을 반납받아 pool에 저장하는 방식

 

 

JDBC : Java Data Base Connectivity. 한글로 번역하면, 자바 데이터 베이스 연결, 정의는 DataBase와 연결하기 위한 Java Interface이다.

 

DBCP : DataBase Connection Pool. 역시 간단하게 정리하면 DataBase와 Connection을 맺고 있는 객체를 관리하기 위한 Connection Pool

 

 

Connection Pool 사용 이유

  • 자바에서 db에 직접 연결해서 처리하는 경우(JDBC) 드라이버를 로드하고 커넥션 객체를 받아와야 한다.
  • 그러면 매번 사용자가 요청할 때마다 드라이버를 로드하고 커넥션 객체를 생성하여 연결하고 종료하기 때문에 매우 비효율적이다.
  • 이러한 문제를 해결하기 위해 커넥션풀을 사용한다.

 

커넥션 풀 특징

  1. 웹 컨테이너(WAS)가 실행되면서 connection 객체를 미리 pool에 생성해 둡니다.
  2. HTTP 요청에 따라 pool에서 connection객체를 가져다 쓰고 반환한다.
  3. 이와 같은 방식으로 물리적인 데이터베이스 connection(연결) 부하를 줄이고 연결 관리 한다.
  4. pool에 미리 connection이 생성되어 있기 때문에 connection을 생성하는 데 드는 요정 마다 연결 시간이 소비되지 않는다.
  5. 커넥션을 계속해서 재사용하기 때문에 생성되는 커넥션 수를 제한적으로 설정함

 

DB Connection 구조

  • 2Tier - 클라이언트로서의 자바 프로그램(JSP)이 직접 데이터베이스 서버로 접근하여 데이터를 액세스하는 구조
  • 3Tier - 자바 프로그램과 데이터베이스 서버 중간에 미들웨어 층을 두어, 그 미들웨어 층에게 비즈니스 로직 구현부터 트랜잭션 처리, 리소스 관리 등을 전부 맡기는 구조이다.

 

JDBC(Java Database Connectivity)

  • 자바 언어로 다양한 종류의 관계형 데이터베이스에 접속하고 SQL문을 수행하여 처리하고자 할 때 사용되는 표준 SQl 인터페이스 API
  • 원래라면 DB마다 연결 방식과 통신 규격이 따로 있기 때문에 프로그램을 DB와 연결한다면, 해당 DB와 관련된 기술적 내용을 배우고 DB가 변경될 시 많은 변경 사항이 존재한다.
  • 각 DBMS에 맞는 JDBC를 받아주게 되면 쉽게 DBMS를 변경할 수 있게 된다.
  • 즉, DBMS 종류(MySQL, MsSQL, Oracle 등)에 상관 없이 하나의 JDBC API를 사용해서 데이터베이스 작업을 처리할 수 있게 된다. JDBC API를 사용하는 애플리케이션의 개략적인 구조는 다음과 같다.

자바 애플리케이션에서 데이터베이스에 접근하기 위해서는 JDBC API를 이용해서 데이터베이스에 접근하고, JDBC API는 JDBC 드라이버를 거쳐 데이터베이스와 통신을 한다.

• 각각의 DBMS는 자신에게 알맞은 JDBC 드라이버를 제공하고 있다.

 

동시 접속자가 많을 경우

  • 동시 접속 할 경우 POOL에서 미리 생성된 connection을 제공하고 없을 경우는 사용자는 connection이 반환될 때까지 번호순대로 대기상태로 기다린다.
  • 여기서 WAS에서 커넥션풀을 크게 설정하면 메모리 소모가 큰 대신 많은 사용자가 대기시간이 줄어들고, 반대로 커넥션 풀을 적게 설정하면 그 만큼 대기시간이 길어진다.

 

커넥션풀 종류

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

 

commons-dbcp

  • Apache Commons
  • 아파치에서 제공해주는 대표적 Connecrion Pool 라이브러리

 

tomcat-jdbc-pool

  • tomcat
  • tomcat에 내장되어 사용되고 있다
  • Apache Commons DBCP 라이브러리를 바탕으로 만들어짐

 

HikariCP

maxLifeTime(커넥션이 커넥션 풀에 최대로 머무를 수 있는 시간)의 값은 mysql의 wait_timeout 보다 몇초정도 짧게 설정한다. (뒤에서 자세히 설명)

  • maxLifeTime을 mysql의 wait_timeout보다 짧게 설정한다면, mysql이 회수하기전에 커넥션 풀이 커넥션을 새롭게 맺으므로 별다른 이슈가 없다.
  • maxLifeTime의 기본값은 30분, mysql의 wait_timeout 기본값은 8시간이다.
  • maxLifeTime으로 커넥션이 끊기고 새롭게 커넥션을 맺는 대상은 active하지 않은 커넥션들 대상이다. (따라서 계속 active 상태의 커넥션이라면 대상이 되지 않는다.)
  • maxLifeTime에 모든 커넥션들이 내려가고 새롭게 맺지 않는다. 순차적으로 이뤄진다.
  • connection pool size를 thread 개수보다 넉넉히 가져가준다. (Hikari CP 데드락 이슈)
  • minimumIdle의 기본 값은 maximumPoolSize이므로, idleTimeout을 설정해주지 않는 이상 따로 손보지 않아도 된다.
  • leakDetectionThreashold는 커넥션이 설정 시간보다 길게 잡고 있다면 누수로 판단하고 WARN 로그를 출력한다.
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)

 

커넥션 풀의 크기와 성능

  • 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 개수를 지정하였다.
      • 코어수에 가까울수록 = 더 적은 수의 스레드가 더 많은 스레드보다 더 나은성능을 발휘
        • blocking이 있을경우에만 더 많은 스레드가 유리함
  • effective_spindle_count는 기본적으로 DB 서버가 관리할 수 있는 동시 I/O 요청 수이다.
    • 하드 디스크 하나는 spindle 하나를 갖는다.
    • 디스크가 16개 있는 경우, 시스템은 동시에 16개의 I/O 요청을 처리할 수 있다.
 Connection을 사용하는 주체인 Thread의 개수보다 커넥션 풀의 크기가 크다면 사용되지 않고 남는 커넥션이 생겨 메모리의 낭비가 발생하게 된다.MySQL의공식레퍼런스에서는 600여 명의 유저를 대응하는데 15~20개의 커넥션 풀만으로도 충분하다고 언급하고 있다. MySQL은 최대 연결 수를 무제한으로 설정한 뒤 부하 테스트를 진행하면서 최적화된 값을 찾는 것을 추천한다.우아한 형제들 테크 블로그에서는 다음과 같은 공식을 추천하고 있다.

  • Tn = 전체 Thread의 개수
  • Cm = 하나의 Task에서 동시에 필요한 Connection 수(단일 스레드가 보유하는 최대 동시 연결 수)
  • -1 : 마지막 connection이 필요한 sub Transaction에 대해
  • +1 : connection 1개가 마지막 sub transaction을 해결할 수 있게 해준다.

 

PoolSize = Tn × ( Cm - 1 ) + ( Tn / 2 ) ← 기본 basic을 배민에서 확장한 방식, basic을 그대로 따라가기 보다는 배민과 같이 실제 서비스에서 +알파로 성능테스트를 통해 최적의 값을 찾아야한다.

 

  1. 여유있게 커넥션풀을 설정하여 성능 높임 & 교착상태방지
  2. thread count : 16
  3. simultaneous connection count : 2
  4. pool size : 16 * ( 2 – 1 ) + (16 / 2) = 24
  5. Dead lock을 피할수 있는 pool 갯수 + a

 

  • 장기 실행 트랜잭션과 매우 짧은 트랜잭션이 혼합된 시스템은 일반적으로 모든 연결 풀로 조정하기 가장 어렵다. 이러한 경우 두 개의 풀 인스턴스를 만드는 것이 잘 작동할 수 있다(예: 하나는 장기 실행 작업용, 다른 하나는 "실시간" 쿼리용).
  • 주로 장기 실행 트랜잭션이 있는 시스템에는 한 번에 특정 수의 작업만 실행하도록 허용하는 작업 실행 대기열과 같이 필요한 연결 수에 대한 "외부" 제약 조건이 있는 경우가 많다. 이러한 경우 작업 대기열 크기는 풀과 일치하도록 "적절한 크기"여야 한다(반대보다는).
  • 10개의 연결 풀 만으로도 6000TPS로 간단한 쿼리를 실행하는 3000명의 프런트 엔드 사용자를 쉽게 처리할 수 있다.
  • 부하 테스트를 실행하면 연결 풀을 줄일수록 TPS 속도가 떨어지기 시작하고 프런트 엔드 응답 시간이 증가하기 시작하는 것을 볼 수 있다.

 

dead lock 확인

  1. HikariCP의 Maximum Pool Size을 1로 설정한 다음 1건씩 Query를 실행해 본다. 만약 정상적으로 실행되지 않고, connection timeout과 같은 에러가 발생한다면 Dead lock 발생 가능성이 있는 코드입니다.
  2. Nested Transaction을 사용하지 않는다!
    1. why? → 보이지 않는 dead lock을 유발할 수 있습니다.

 

참고 

https://techblog.woowahan.com/2663/

'CS' 카테고리의 다른 글

Cold Stream vs Hot Stream 이란?  (1) 2023.10.31
API 헬스체크  (0) 2023.08.17
WebFlux  (0) 2022.11.06
pinPoint  (0) 2022.11.06
JWT 토큰이란  (0) 2022.05.28