DBCP(DataBase Connection Pool)

이재민·2023년 12월 26일

DBCP

DBCP란, DataBase Connection Pool의 약자이며 여러개의 데이터베이스 커넥션을 미리 생성 후 필요할 때 마다 할당해줍니다.

DataBase Connection Pool은 웹 애플리케이션에서 필수 요소입니다.
Connection Pool 오픈소스 라이브러리로 Apache의 Commons DBCP, Tomcat-JDBC, BoneCP, HikariCP 등이 있습니다.

Connection Pool 라이브러리를 잘 사용하면 데이터베이스와 애플리케이션 사이에서 발생하는 서비스 전체로 전파되지 않게 할 수 있고, 긴 시간의 장애로 이어지지 않게 할 수 있습니다.
반면, 적절하지 못한 설정을 통해 Connection Pool이 애플리케이션에서 병목 지점이 되는 경우가 생길 수 있습니다.
웹 애플리케이션의 요청은 대부분 DBMS로 연결되기 때문에 Connection Pool 라이브러리의 설정은 전체 애플리케이션의 성능과 안정성에 영향을 미치는 요소입니다.

출처:https://github.com/brettwooldridge/HikariCP-benchmark?tab=readme-ov-file

DBCP 구성 전

DBCP를 구성하지 않는다면 여러 문제가 발생할 수 있습니다.
DataBase Connection을 맺고 끊기 위해선 TCP 기반의 통신이 이뤄지고 3,4 Way-Handshake 과정이 필요합니다.
커넥션이 필요할때 마다 매번 Handshake과정이 이뤄지면 시간적인 비용이 발생하여 Client에게 응답시간이 지연되게 됩니다.
이로 인해 서비스의 성능에 좋지 않은 영향이 발생할 수도 있습니다.

DBCP 동작방식

위와 같은 문제로 인해 Application Server가 기동될때 DBCP을 구성하고 커넥션 풀 관련 파라미터를 조정하여 서비스 성능을 향상시킬 수 있습니다.
1. SW Application Server를 띄울때 DB Connection을 미리 생성합니다.
2. 미리 연결된 Connection들을 Pool로 관리하게 됩니다.
3. 생성된 Pool에서 놀고 있는(Idle) Connection을 사용하여 API 요청 처리를 진행합니다.
4. 사용 완료된 Connection을 close할때는 실제 Connection을 끊는 것이 아니라 Connection Pool에 반환을 합니다.
이로써 Connection을 재사용하고 Connection 맺고, 끊는 시간을 절약하여 서비스의 성능과 Client에게 응답 속도를 높일 수 있습니다.


MySQL DB Config(DB Server Config)

max_connections

  • client와 맺을 수 있는 최대 connection 수.
    DBCP의 설정과 max_connections 수가 동일할 경우 DBCP의 connection이 요청을 커버하지 못해 추가로 Application Server를 띄우고 DB에 요청을 하지만, DB Server의 max_connections 수가 부족해 신규 Application Client가 DB Server와 맺을 수 있는 커넥션이 없을 수 있습니다.
  • 적절한 max_connections 설정이 필요합니다.
  • AWS Aurora MySQL default max_connections

wait_timeout

  • connection이 in-active할 때 다시 요청이 오기까지 얼마의 시간을 기다린 뒤에 connection close할 것인지 결정하는 설정.
    비정상적인 connection 종료, connection 반환 안됨, 네트워크 단절 등 DB Server에 연결된 connection이 open된 채로 기다리고 있는데, 해당 설정을 통해 언제 close를 시킬지 설정하는 값입니다.

HikariCP DBCP Config

가장 많이 사용되는 DBCP 프레임워크인 HikariCP 설정을 알아보겠습니다.
HikariCP는 SpringBoot에 기본적으로 내장되어 있는 JDBC 데이터베이스 커넥션 풀링 프레임워크입니다.
SpringBoot는 HikariCP를 사용할 수 있는 상황에서는 항상 HikariCP를 선택한다고 합니다.
HikariCP

minimumIdle

  • pool에서 유지하는 최소한의 Idle Connection 수

  • Idle Connection 수가 minimumIdle보다 작고 전체 Connection 수도 maximumPoolSize보다 작다면 추가로 connection을 생성합니다.

    	```
    	ex) minimumIdle: 2, maximumPoolSize: 4
    	최초 connectionPool에는 2개의 Idle Connection을 생성합니다.
    	이후 Aplication Server로 요청이 들어오고 이를 처리하기 위해 connection이 하나 in-use 상태로 바뀌게 되면
    	minimumIdle 설정을 유지하기 위해 새로운 connection이 생성하게 됩니다.
    	위의 케이스처럼 계속해서 connection 상태가 in-use되고, minimumIdle 설정을 준수하기 위해 새로운 connection이 생성될지라도
    	maximumPoolSize를 초과할 수는 없습니다.
    
    	우선순위: maximumPoolSize > minimumIdle

하지만 minimumIdle 사이즈와 maximumPoolSize를 동일하게 설정하는 것을 HikariCP에서 권장합니다.
minimumIdle 사이즈는 default 값으로 maximumPoolSize와 동일합니다.
앞서 말씀드렸듯이 트래픽이 물리게되면 비용이 높은 추가 connection을 생성해야 합니다.
또한 connection을 생성하는 속도보다 트래픽이 몰리는 속도가 더 빠르게 된다면 추가 connection을 맺고, 끊는 시간으로 인해 client에게 응답이 느려질 수도 있기 때문입니다.

maximumPoolSize

  • connection pool이 가질 수 있는 최대 connection 수
  • 이는 Idle과 active(in-use) connection 합친 최대 수

maxLifetime

  • connection pool에서 connection의 최대 수명
  • maxLifetime을 넘기면 Idle일 경우 pool에서 바로 제거가되고 active인 경우 pool로 반환된 후 제거됩니다.
  • connection pool 전체가 아닌 connection 별로 해당 설정이 적용됩니다.
    pool에서 대량의 connection이 제거되는 것을 방지하기 위함입니다.
  • maxLifetime 동작으로 인해 connection이 제거되어도 DBCP 사이즈에 맞게 바로 connection이 생성된다고 생각하시면 됩니다.
  • maxLifetime은 in-use 상태에서는 적용되지 않기에 connection을 제대로 pool에 반환해주는 것이 중요합니다. (아래 케이스 참고)
    	코드상에 버그로 인해 Application -> DB Server로의 요청을 수행하는 connection이 제대로 반환되지 않았고
    	DB Server의 wait_timeout이 초과된 경우 DB Server에서도 해당 connection을 제거하게 됩니다.
    	만약 Application에서 추가 요청이 들어왔고 제거된 connection이 이를 수행하게 된다면
    	DB Server에서는 제거한 connection이기에 Exception이 발생할 수 있습니다.
  • DB connection time limit 보다 대략 2~3초 혹은 5초 정도 짧게 설정해야 합니다.

connectionTimeout

  • connection pool에서 connection을 받기 위한 대기 시간
    Application에 요청이 발생하고 이를 처리하기 위해 pool에서 connection을 받아야 할때 connection을 받기까지 대기하는 시간
    	connectionTimeout: 30s
    	Application에 요청된 request를 처리하기 위해 DBCP에서 Idle Connection을 할당받는데
    	트래픽이 증가하고 DBCP에 Idle Connection이 존재하지 않을때 30s를 기다린 후에 Exception이 발생하게 됩니다.
  • 해당 설정은 서비스마다 다를 수 있습니다.
    일반적인 서비스의 사용자로부터 들어온 요청의 경우 30s를 기다리지는 않을 것입니다.
    많이 기다려야 5~10초를 기다린 후 서비스를 나갈텐데 이렇게 되면 client로 부터 connection이 끊기게 됩니다.
    29s에 connection을 받고 처리 후 client에 반환을 하더라도 이미 client 요청이 없기에 이는 너무 긴 시간의 설정 값일 수 있습니다.

적절한 connection 수 찾기

부하테스트를 통해 적절한 connection 수를 찾는 방법

  • 모니터링을 통해 서버 리소스, 서버 스레드 수, DBCP 등을 모니터링할 수 있습니다.
  • request per second(이하 RPS), avg response time(이하 ART)을 확인할 수 있습니다.
  1. Application의 cpu 또는 memory 사용량이 증가한다면 추가 Instance를 생성하여 Application이 갖는 부하를 분산시킬 수 있습니다.
  2. DB Server의 cpu 또는 memory 사용량이 증가한다면 Replication구성의 경우에는 Secondary DB를 추가할 수 있고, Cache Layer 도입, Sharding 등의 방법을 이용해 DB Server의 부하를 낮출 수 있습니다.
  3. 1, 2번의 상태가 괜찮은데 여전히 RPS, ART의 그래프 지표가 나아지지 않는다면 Thread Pool의 상태도 체크해 볼 수 있습니다.
    • Spring MVC의 경우 Thread per request 모델을 사용중인데 Thread Pool의 Thread가 병목이 발생할 수 있기 때문입니다.
    • Thread Pool의 Thread 카운트가 너무 적어 병목이 발생할 수 있기에 Thread Pool Size를 조절할 수 있습니다.
  4. 1, 2, 3번까지 확인 했다면 DBCP의 in-use Connection 수를 확인해봅니다.
    • maximumPoolSize가 5개인데, in-use Connection도 5개라면 maximumPoolSize가 너무 낮을 수도 있습니다.
    • maximumPoolSize에 맞춰 DB Server릐 max_connections 설정을 조절할 수 있습니다.
  • 이와 같이 부하테스트를 통해 다양한 리소스를 파악하고 설정을 한다면 DBC의 적절한 max pool size를 결정할 수 있습니다.
profile
문제 해결과 개선 과제를 수행하며 성장을 추구하는 것을 좋아합니다.

0개의 댓글