데이터베이스 커넥션 풀 (Database Connection Pool)
한눈에 보기
데이터베이스 연결을 미리 만들어 재사용하는 커넥션 풀의 동작 원리와 필요성을 다룬다. HikariCP 설정, 풀 크기 계산, 타임아웃과 고갈 문제를 실무 관점에서 정리했다.
최종 업데이트 2026.09.17
커넥션 풀(Connection Pool)은 데이터베이스 연결을 요청할 때마다 새로 만드는 대신, 미리 일정 개수의 연결을 만들어 두고 애플리케이션이 빌려 쓰고 반납하는 방식으로 재사용하는 기법이다. 연결 생성에 드는 네트워크 왕복과 인증 비용을 없애 응답 시간을 줄이고, 데이터베이스에 동시에 맺을 수 있는 연결 수를 일정 범위로 통제하는 역할을 한다. 자바 진영에서는 HikariCP가 사실상 표준 구현으로 쓰인다.
연결 하나를 만드는 데 드는 비용
데이터베이스 연결은 단순한 객체 생성이 아니다. 클라이언트와 서버 사이에 TCP 3방향 핸드셰이크(3-way handshake)가 일어나고, TLS를 쓴다면 암호화 핸드셰이크가 추가되며, 그 위에서 데이터베이스 프로토콜 수준의 인증과 세션 초기화가 이어진다. 이 과정은 수 밀리초에서 수십 밀리초가 걸리는데, 쿼리 자체가 1밀리초 안에 끝나는 경우가 많다는 점을 생각하면 배보다 배꼽이 큰 구조다.
서버 쪽 부담도 크다. 데이터베이스는 연결마다 세션 메모리, 정렬 버퍼, 프로세스나 스레드 같은 자원을 할당한다. 요청이 몰릴 때마다 연결을 새로 만들면 데이터베이스가 연결 폭주만으로 무너질 수 있다. 그래서 연결을 미리 만들어 재사용하고, 동시에 상한을 두어 데이터베이스를 보호하는 커넥션 풀이 필요하다.
풀의 동작 원리
커넥션 풀의 생명 주기는 대여와 반납으로 요약된다. 동작 순서는 다음과 같다.
- 애플리케이션 시작 시 풀이 최소 개수의 연결을 미리 만들어 유휴(idle) 상태로 보관한다.
- 스레드가 연결을 요청하면 풀은 유휴 연결 하나를 꺼내 건네준다. 이때 연결이 살아 있는지 검증 쿼리나 저수준 확인으로 점검하기도 한다.
- 유휴 연결이 없고 최대 개수에 도달하지 않았다면 새 연결을 만들어 준다.
- 최대 개수까지 모두 사용 중이면 요청 스레드는 정해진 대기 시간(connection timeout)만큼 블록되고, 그 안에 반납이 없으면 예외를 받는다.
- 애플리케이션이 사용을 마치면 연결은 닫히는 것이 아니라 풀로 반납되어 다음 요청에 재사용된다.
개발자가 코드에서 호출하는 close는 실제 연결 종료가 아니라 반납이라는 점이 핵심이다. 풀은 별도로 유휴 시간이 오래된 연결이나 최대 수명(max lifetime)을 넘긴 연결을 조용히 폐기하고 새로 채워, 방화벽이나 데이터베이스가 오래된 연결을 끊어 생기는 문제를 예방한다.
HikariCP가 빠른 이유
HikariCP는 스프링 부트(Spring Boot)의 기본 커넥션 풀로 채택된 경량 구현이다. 성능의 비결은 화려한 기능이 아니라 극단적인 단순화다. 내부적으로 락 경합을 줄이도록 설계된 자료 구조인 ConcurrentBag을 사용해, 스레드가 직전에 쓰던 연결을 우선 다시 잡게 하여 동기화 비용을 줄인다. 또 프록시 생성과 바이트코드 수준 최적화로 연결을 감싸는 오버헤드를 최소화했다. 설정 항목도 의도적으로 적게 유지해 잘못 조합할 여지를 줄였다. 대표 설정은 다음과 같다.
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.connection-timeout=3000
spring.datasource.hikari.max-lifetime=1800000
HikariCP는 minimum-idle을 maximum-pool-size와 같게 두는 고정 크기 풀을 권장한다. 풀 크기가 수시로 늘었다 줄었다 하면 트래픽이 몰리는 순간마다 연결 생성 비용을 다시 치르기 때문이다.
풀 크기는 어떻게 정하는가
풀을 크게 잡을수록 좋다고 생각하기 쉽지만 실제로는 반대에 가깝다. 데이터베이스 서버가 실제로 동시에 처리할 수 있는 작업 수는 CPU 코어 수와 디스크, 네트워크 대기 시간에 의해 제한된다. 연결이 코어 수보다 훨씬 많으면 컨텍스트 스위칭과 락 경합만 늘어나 오히려 전체 처리량이 떨어진다. HikariCP 문서가 소개하는 경험적 출발점은 다음 공식이다.
pool size = (CPU core 수 * 2) + 유효 디스크 스핀들 수
예를 들어 코어 8개짜리 데이터베이스 서버라면 풀 크기 20 안팎에서 시작해 부하 테스트로 조정하는 식이다. 주의할 점은 이 상한이 데이터베이스 기준이라는 것이다. 애플리케이션 인스턴스가 10대이고 각 풀이 20이면 데이터베이스에는 최대 200개의 연결이 맺힌다. 따라서 인스턴스 수를 곱한 총합이 데이터베이스의 max connections와 실제 처리 능력 안에 들어오도록 설계해야 한다.
풀 고갈 장애 패턴
운영에서 가장 자주 만나는 커넥션 풀 장애는 고갈(exhaustion)이다. 모든 연결이 사용 중이라 새 요청이 대기하다가 타임아웃 예외를 쏟아내는 상황으로, 원인은 대체로 다음 중 하나다.
| 패턴 | 원인 | 대응 |
|---|---|---|
| 연결 누수 | 예외 경로에서 반납이 누락되어 연결이 영영 돌아오지 않는다 | try-with-resources 사용, leak detection threshold 설정 |
| 느린 쿼리 점유 | 인덱스 없는 쿼리나 락 대기가 연결을 오래 붙잡는다 | 쿼리 튜닝, 쿼리 타임아웃 설정 |
| 트랜잭션 안 외부 호출 | 트랜잭션을 연 채로 외부 API를 호출해 응답까지 연결을 점유한다 | 트랜잭션 범위 축소, 외부 호출 분리 |
| 순간 트래픽 폭주 | 동시 요청이 풀 크기를 크게 넘는다 | 스케일 아웃, 큐잉, 서킷 브레이커 |
풀 크기를 늘리는 것은 마지막 수단이다. 누수나 느린 쿼리가 원인이라면 풀을 키워도 고갈 시점만 늦춰질 뿐 같은 장애가 반복된다. 활성 연결 수, 대기 스레드 수, 대여 대기 시간 같은 풀 지표를 모니터링에 반드시 포함해야 원인을 빠르게 좁힐 수 있다.
면접에서 이렇게 물어본다
- 커넥션 풀을 쓰지 않고 요청마다 연결을 새로 만들면 어떤 문제가 생기는지 설명해 보라.
- 커넥션 풀의 최대 크기를 무작정 크게 잡으면 왜 성능이 오히려 나빠질 수 있는가.
- 운영 중 커넥션 풀 고갈로 타임아웃이 발생한다면 어떤 순서로 원인을 좁혀 가겠는가.
- 트랜잭션 안에서 외부 API를 호출하는 코드가 커넥션 풀에 어떤 영향을 주는가.
- HikariCP가 다른 풀 구현보다 빠른 이유를 아는 대로 설명해 보라.
데이터베이스에서 같이 읽기
- 이상 현상 (Anomaly)이상 현상은 테이블 설계가 잘못되어 삽입, 갱신, 삭제 과정에서 데이터 불일치나 손실이 생기는 현상이다. 삽입, 갱신, 삭제 이상의 세 종류와 원인인 함수 종속, 해결책인 정규화까지 예시와 함께 정리한다.
- 인덱스 (Index)인덱스는 컬럼 값과 저장 위치를 별도 자료구조로 정리해 검색 속도를 높이는 장치다. B+Tree 동작 원리, 클러스터드와 논클러스터드 비교, 복합 인덱스와 커버링 인덱스, 설계 시 주의점을 다룬다.
- 데이터베이스 레플리케이션 (Database Replication)원본 데이터베이스의 변경을 레플리카에 복제해 읽기 부하와 장애를 분산하는 구조를 다룬다. 동기와 비동기 복제, 복제 지연, 읽기 일관성과 장애 전환을 정리했다.
- 데이터베이스 키 (Key)의 종류데이터베이스 키는 행을 유일하게 식별하거나 테이블 간 관계를 표현하는 속성 집합이다. 유일성과 최소성을 기준으로 슈퍼키, 후보키, 기본키, 대체키, 외래키를 구분하고 자연키와 대리키 선택 기준까지 정리한다.
이 개념, 내 코드에서는 어떻게 물어볼까요
데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.
공고 넣고 시작하기