데이터베이스

[DB] DB Connection Pool

위시리 2025. 4. 21. 00:32
사용 배경

Database Connection

  • 커넥션 : 어플리케이션과 데이터베이스의 연결을 의미
  • 어플리케이션에서 데이터베이스에 접속하고 종료하는 일련의 과정을 의미

  • 웹 어플리케이션과 데이터베이스는 서로 다른 시스템으로, 이를 연결하기 위해 데이터베이스 드라이버 사용
  • 데이터베이스 연결의 생애 주기
    1. 데이터베이스 드라이버를 사용하여 데이터베이스 연결 열기
    2. 데이터를 읽고 쓰기 위한 TCP 소켓 열기
    3. TCP 소켓을 사용해 SQL 요청/응답 통신
    4. 데이터베이스 연결 닫기
    5. TCP 소켓 닫기 (DB 연결 종료될 때 소켓도 함께 닫힘)
  • TCP기반 통신 시, 매번 connection을 열고 닫는 (3-way-handshake, 4-way-handshake) 시간적인 비용 발생으로 서비스 성능 저하
효율적인 커넥션 관리는 성능과 안정성 측면에서 중요한 요인으로, 시스템의 많은 부하를 주는 커넥션을 여닫는 비용을 해결하기 위해 DBCP 등장

 

✅ TCP 기반 DB 커넥션에서 부하가 발생하는 이유

1. 커넥션 생성/해제의 비용

  • TCP는 연결지향형 프로토콜이기 때문에, 커넥션을 만들기 위해 3-way handshake, 커넥션을 닫을 때는 4-way handshake라는 절차가 필요
  • 이 과정에서 네트워크 패킷 교환이 발생하고, 그에 따른 CPU 사용 및 I/O 자원 소모가 생긴다.
  • 이런 커넥션이 요청마다 매번 새로 생성되고 닫히면, 이 비용이 누적되어 시스템 전체에 큰 부하를 줄 수 있다.

2. 자주 여닫는 I/O 자원의 비효율

  • 데이터베이스 커넥션은 내부적으로 소켓과 파일 디스크립터 같은 리소스를 사용
  • 요청이 올 때마다 DB 커넥션을 열고 닫으면, OS가 매번 해당 리소스를 생성/해제해야 하므로 컨텍스트 스위칭, 메모리 할당/해제가 빈번하게 일어나고, 이는 성능 저하로 이어진다.

3. 다중 사용자 환경에서의 병목

  • 웹 서비스는 동시 접속이 많다.
    사용자가 1000명이 동시에 접속해서 각각 DB에 연결하려 한다면, 1000개의 TCP 커넥션이 순식간에 생기고 없어지는 일이 반복
  • 데이터베이스 서버는 그 많은 커넥션을 하나하나 처리하기 위해 CPU, 메모리, 스레드를 더 많이 사용해야 하고, 결국 과부하로 이어질 수 있다.

 

 

DBCP

" DBCP는 미리 여러 개의 커넥션을 만들어, 요청이 올 때마다 빌려줬다가 반환받는 방식 "
" 매번 커넥션을 열고 닫는 비용을 줄이고, 커넥션을 재사용해 성능을 향상시키기 위해 사용 "

매번 커넥션을 생성하고 소멸하면서 DB에 연결하는 방식은 서비스 성능을 저하시킬 수 있다. 이런 문제를 해결하기 위해 DBC가 등장했다. DBCP는 미리 TCP 커넥션을 만들어서 Pool에 저장해두고, 필요할 때마다 꺼내서 재사용하는 방식을 의미한다. TCP 소켓과 달리, 매번 새로 생성하고 닫는 것이 아니라 이미 만들어진 연결을 사용하기 때문에 시스템 리소스 소모가 훨씬 줄어든다.

 

 개념

  • DataBase Connection Pool의 약자로, DB와 connection을 맺고 있는 객체를 관리
  • 웹 컨테이너가 실행되면서 DB와 미리 connection을 해놓은 객체들을 pool에 저장
  • 클라이언트 요청 시 connection을 빌려주고, 처리가 끝나면 해당 connection을 반납받아 pool에 저장
웹 컨테이너 (Web Container)

Servlet이나 JSP(Java Server Pages)와 같은 웹 컴포넌트를 실행해주는 소프트웨어 환경
자바 기반 웹 어플리케이션이 돌아갈 수 있게 HTTP 요청/응답 처리를 하고, 서블릿 생명 주기를 관리

ex) Apache Tomcat

 

 특징

  • HTTP 요청에 따라 pool에서 connection 객체를 사용하고 반납
  • 물리적인 데이터베이스 connection 부하를 줄이고 연결 관리
  • pool에 미리 생성된 connection으로, 요청마다 connection을 생성하지 않아 연결 시간이 소비되지 않음
  • connection을 계속 재사용하므로 생성되는 connection 수 제한적으로 설정
  • DBCP 사용 시에도 교착상태(Deadlock)에 주의해야 한다.
    • 스레드가 서로의 DB Connection이 반납되기만을 무한정 대기하게되며 발생
    • gpt
      • 여기서의 교착상태는 스레드 간의 락 충돌보다는, 커넥션을 기다리며 무한 대기하는 상태
      • DB 커넥션이 모두 사용 중인 상황에서 새로운 요청들이 커넥션을 얻지 못하고 서로 무한정 기다리는 상태

 

 

DBCP 동작 방식 (HikariCP)

📌 STEP 1. Thread가 Connection을 요청하여 Connection Pool에 유휴 Connection을 찾아 반환한다.

HikariCP 의 경우, 이전에 사용한 Connection 이 존재하는지 확인하여, 이를 우선 반환하는 특징이 있다.

 

📌 STEP 2. 가능한 Connection이 존재하지 않으면, HandOffQueue를 Polling하면서 다른 Thread의 Connection 반납을 기다린다.

지정한 Timeout 시간까지 대기하다 시간이 만료되면 예외를 던진다. (Polling 방식)


EX

  • 스레드 A가 커넥션 요청
  • 그런데 Connection Pool에 사용 가능한 커넥션이 없는 상태 (모든 커넥션 사용중)
  • HikariCP는 "지금 누군가가 커넥션을 반납할 때까지 기다려야겠다" 라고 판단
  • HandOffQueue라는 대기열(queue)에 들어가서, 다른 스레드가 커넥션을 반납해줄 때까지 대기

Polling 방식이란

  • HikariCP 는 무한히 기다리는 것이 아니라, 짧은 주기로 계속 체크한다.
  • handOffQueue.poll(timeout) 같은 방식으로 타임아웃까지 기다리며 반납된 커넥션을 받는데,
  • 설정된 timeout 내에 반납이 없으면 예외(ConnectionTimeoutException)를 발생시켜 개발자가 감지할 수 있도록 한다.

 

📌 STEP 3.

  • 사용한 Connection 이 반납되면 Connection Pool이 Connection 사용 내역을 기록하고, HandOffQueue에 반납된 Connection을 넣는다.
  • 이를 통해 HandOffQueue를 Polling 하던 대기 Thread가 Connection을 획득하고 작업을 이어나간다.

 

  • WAS에서 Connection을 사용하는 주체는 스레드
    • WAS는 스레드로 요청을 처리
    • 각 스레드는 DB와 대화하기 위해 Connection 필요
    • 그 Connection을 Pool에서 빌려서 사용
  • 데이터베이스에서 Connection의 수는 스레드의 수와 어느정도 일치
    • Connection이 많다는 것은 데이터베이스 서버가 스레드를 많이 사용한다는 것을 의미
    • 이에 Context Switching으로 인한 오버헤드가 더 많이 발생하기에 Connection Pool을 많이 늘려도 성능적인 한계 존재

 

Connection Pool의 크기

1. 크게 설정

⚡ 성능 사용자 대기 시간 줄어듦 (connection 부족 현상이 덜함)
💥 문제 DB도 많은 connection을 처리하려고 더 많은 스레드를 쓰게 됨 → 문맥 교환 증가 → DB 성능 저하

스레드가 사용하는 connection 외에 남은 connection 은 메모리 공간만 차지
💾 메모리 사용 많아짐 (connection이 많으니까) == 메모리 소모 큼
example

Connection Pool을 500개로 늘려두면, 동시에 500개의 요청을 병렬로 처리 가능 하지만 DB 입장에서는 500개의 연결을 동시에 처리해야 하므로 내부적으로도 스레드가 500개쯤 쓰이게 되고, CPU는 그 스레드들을 계속 번갈아 실행 → Context Switching 증가 → 성능 저하

 

2. 작게 설정

💾 메모리 사용 적음 (Connection이 적으니까)
⚡ 성능 사용자가 기다리는 시간이 길어짐 (Connection을 기다려야 해서)
✅ 장점 DB에 과한 부하가 가지 않음 (관리할 연결 수가 적음)
example

Pool을 10개만 설정해두면, 동시에 처리 가능한 요청은 10개뿐
11번째 요청은 1개가 끝날 때까지 기다려야 함 → 사용자 응답 지연 발생

 

WAS (Web Application Server) : 클라이언트(브라우저 등) 요청을 받아 처리하는 서버

 

 

DBCP프레임워크

종류

  • Apache Commons DBCP
  • Tomcat DBCP
    • Tomcat에서 내장되어 사용됨
    • Apache Commons DBCP 라이브러리를 바탕으로 만들어짐
  • Oracle UCP
  • HikariCP

 

 HikariCP

  • 가벼운 용량과 빠른 속도를 가지는 JDBC 데이터베이스 커넥션 풀 프레임워크
  • spring-boot에 기본적으로 내장
    • spring-boot는 항상 HikariCP를 우선적으로 사용
    • HikariCP를 사용할 수 없는 경우, Tomcat DBCP → Apache Commons DBCP → Oracle UCP 순으로 선택
  • HikariCP는 바이트코드 수준까지 극단적으로 최적화되어있기에 가장 많이 사용된다.

 

주요 DB 서버 파라미터 (HikariCP)

  • max_connections
    • 클라이언트와 맺을 수 있는 최대 connection 수
    • 신규 서버 투입, DBCP connection 수 늘릴 때 등 문제 없이 동작하기 위해서 적절한 값 설정
  • wait_time 
    • DB 서버에서 connection이 inactive 할 때, 다시 요청이 오기까지 얼마의 시간을 기다린 뒤에 close를 할 것인지 결정
    • 일정시간을 기다린 후에도 요청이 오지 않을 경우 연결 해제
      • DBCP의 어떤 connection이 비정상적이 상태일 때, DB 서버는 이를 모르기 때문에 계속 대기하는 것을 방지하기 위함

 

 

관련 개념

📌 Data Source

  • javax.sql.DataSource 인터페이스
  • Connection Pool 을 관리하는 목적으로 사용되는 객체
  • Connection Pool 을 어플리케이션 단에서 어떻게 관리할지 구현하는 인터페이스
    • 어플리케이션에서는 해당 Data Source 인터페이스를 통해 Connection을 얻어오고 반납하는 등의 작업 구현

 

📌 JDBC

개념

  • Java DataBase Connectivity, 자바에서 데이터베이스를 조작하는 표준 SQL 인터페이스 API

특징

  • 데이터베이스들은 JDBC를 사용하기 위한 각각의 Driver 제공
  • DBMS 독립성으로, 애플리케이션은 특정 DBMS에 종속되지 않고 JDBC API를 통해 다양한 DBMS와 통신 가능
  • 인터페이스 기반 구축(데이터베이스 커넥션 인터페이스)
  • 일반적인 JDBC Database Pool방식을 사용하지 않고 DB에서 정보를 가져올때마다 Driver를 로드하고 커넥션을 열고닫음
    • 해당 비효율적인 부분을 DBCP를 통해 효율적으로 처리
    • 이런 비효율성으로 상용 어플에서 JDBC방식 거의 사용하지 않음

 

📌 JNDI

개념

  • Java Naming and Directory Interface
  • Java 애플리케이션에서 리소스, 객체 및 서비스에 대한 표준 네이밍 및 디렉터리 서비스에 접근할 수 있게 해주는 API 및 스펙

기능

  • 객체검색
  • 이름과 객체 연결
  • 분산환경 지원

특징

  • 데이터베이스의 DB Pool을 미리 Naming 시켜주는 방법 중 하나
    • WAS에서 JNDI를 활용하여 DBCP를 관리해 데이터베이스 연결관리의 효율성을 높일 수 있음
  • Static 객체 사용
    • JNDI로 설정된 데이터베이스 연결 풀은 WAS 내에서 관리되며, 이는 static 객체로 설정될 수 있음
    • 이러한 정적객체를 사용하여 데이터베이스 연결을 쉽게 가져다 사용 가능
    • 애플리케이션 전체에서 공유되므로 연결을 여러 번 생성할 필요가 없어 효율성 향상
  • 어플리케이션 확장성
    • JNDI를 사용하여 DB 연결을 관리하면 애플리케이션의 확장성이 향상
    • 새로운 애플리케이션 서버 노드 추가 새로운 인스턴스 배포 , JNDI 통해 설정된 연결 풀을 공유하고 확장