Connection Pool(DBCP)

2024. 3. 25. 19:24Database

[점프 투 FastAPI] 2-02에서 db 설정을 하면서 커넥션 풀을 생성한다.

engine = create_engine(
    SQLALCHEMY_DATABASE_URL,
    connect_args={"check_same_thread": False}
)

 

Connection Pool(DBCP)?

  • 미리 db에 접속(connection)한 객체를 pool에 일정 개수만큼 저장 두고, 이 객체들을 돌려가며 사용.
  • db에 접속하는 세션 수 제어.
  • 세션 접속 소요 시간 및 비용 절감.

 

필요성

  • Java에서는 db에 직접 연결해서 처리할 때 드라이버 로드 후 커넥션 객체를 생성해야 한다.
     => 이 경우 사용자의 요청 횟수만큼 db 드라이버 로드 및 커넥션 객체 생성 후 종료해야 하므로 비효율적.
  • Web 애플리케이션에서는 HTTP 요청에 따라 Thread를 생성하고 대부분의 로직은 db 서버로부터 데이터를 얻는다.
     => 이 경우도 요청 횟수만큼 드라이버 로드 및 커넥션 객체 생성을 위해 물리적으로 db 서버에 접근해야 한다.
  • 커넥션 풀을 사용하면 pool에 있는 커넥션 되어 있는 객체를 재사용하여 비용 및 시간 절감.

 

Connection 객체 생성 과정

  1. 애플리케이션 로직 : db 드라이버를 통해 커넥션 조회.
  2. db 드라이버 : db와 TCP/IP 커넥션 연결(3 way handshake 등 네트워크 동작 발생).
  3. db 드라이버 : ID, PW 등 기타 부가정보를 db에 전달.
  4. db : ID, PW로 내부 인증 후 내부에 db 세션 생성.
  5. db : db 드라이버에 커넥션 생성 완료 응답 전송.
  6. db 드라이버 : 생성된 커넥션 객체를 애플리케이션 로직에 반환.
  • db와 애플리케이션 서버에서 TCP/IP 커넥션을 새로 생성하기 위한 리소스를 매번 사용해야 한다.
  • 결국 사용자 관점에서도 SQL 실행 시간 및 커넥션 생성 시간 추가로 느린 응답 속도를 경험하게 된다.

 

Connection Pool(DBCP) 동작

  1. 웹 컨테이너(WAS) 실행 시 커넥션 객체를 설정된 개수만큼 미리 생성하여 Pool에 저장.
  2. HTTP 요청에 따라 Pool에서 커넥션 객체를 사용하고 반환.
  3. 커넥션 수를 제한하고 커넥션을 계속해서 재사용.
  • 물리적인 db 커넥션 부하를 줄이고 연결 관리.
  • 미리 커넥션을 생성하고 재사용하므로 커넥션 생성에 소모되는 시간 및 비용이 적어짐.
  • Pool에 남은 커넥션 객체가 없는 상태로 요청이 발생하면 커넥션이 반환될 때까지 선착순으로 대기 상태.
  • WAS에서 커넥션 풀을 크게 설정하면 메모리 소모가 큰 대신 사용자 대기 시간이 줄어들게 된다.(메모리 소모와 대기 시간 반비례)

 

교착상태(DeadLock)와 적정 Connection Pool 크기

DeadLock : Thread가 서로의 db 커넥션이 반납되기를 무한 대기.

  1. Thread_1이 작업 수행을 위해 2개의 db 커넥션 요청.
  2. Pool의 사이즈는 1(total=1, active=0, idle=1, waiting=0).
  3. Pool에 존재하는 커넥션이 1개이므로 1개만 전달.
  4. Thread_1은 1개의 커넥션으로 작업이 불가하므로 다른 Thread로부터 1개의 커넥션 반납 대기.(하지만 반납될 커넥션은 0개.)
  5. Thread_1은 자신의 커넥션이 반납하는 것을 대기하는 상황. 즉, DeadLock.
교착상태 방지를 위한 DBCP 최소 사이즈 공식
WAS의 Thread 개수
* (Thread가 작업 수행을 위해 동시에 필요한 db 커넥션 개수 - 1) + 1

위 공식은 최솟값이므로 +α가 필요하다.

시스템 환경 및 규모에 따라 적절한 값이 다르기 때문에 성능 테스트를 통해 최적화해야 한다.

 

결론

SQLALCHEMY_DATABASE_URL = "sqlite:///./myapi.db"

engine = create_engine(
    SQLALCHEMY_DATABASE_URL,
    connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(
    autocommit=False, #commit 사인이 있어야 저장, rollback 가능(True면 불가)
    autoflush=False,
    bind=engine
)

create_engine() : db 연결을 위한 Connection Pool 설정. (pool_pre_ping, pool_size, max_overflow 등 옵션이 있다.)

SQLALCHEMY_DATABASE_URL : 사용할 db 설정.

connect_args={"check_same_thread": False} : 하나의 Thread만 통신할 수 있도록 설정. SQLite 사용 시 각 Thread가 독립적으로 요청을 처리하는 가정이기 때문에 Thread 통신이 불가하므로 충돌 방지를 위한 필수 옵션.

Sessionmaker() : 실질적 통신 세션 정의. 이 자체가 db 세션이 아니고 설정값을 넘겨받은 클래스일 뿐이다. 이 클래스의 인스턴스 객체가 생성되면 해당 객체가 db 세션 역할 수행.

 

 

 

 

 

Java로도 작업해보면 좋을 것 같다. 하던 거 다 하면 해봐야겠다.

알수록 알아갈 게 많다는 점에서 좋은 것 같기도, 고수분들에 비해 뒤처진 느낌이라 쫓기는 것 같기도 하다.

어떻게든 되겠지?

'Database' 카테고리의 다른 글

Migration 계획  (0) 2025.07.18