6주차 Unit 4.2 — Connection Pool 개념

Psj·4일 전

F-lab

목록 보기
195/230

Unit 4.2 — Connection Pool 개념

F-LAB JAVA · 6주차 · Phase 4 · Connection Pool과 DB 세션
★ 깊이 파기 — Connection Pool 의 모든 것


📌 학습 목표

이 Unit을 끝내면 다음을 답할 수 있어야 한다.

  • Connection Pool 의 정의는?
  • 풀 동작 (미리 N개, 빌림/반환/재사용) 은?
  • HikariCP 가 Spring Boot 기본인 이유는?
  • 풀 크기 결정 (CPU 코어, 작업 시간) 은?
  • 풀 크기보다 동시 요청이 많으면 ?
  • 대표 구현체 (HikariCP / DBCP2 / Tomcat JDBC Pool) 는?
  • 풀 크기 vs 동시 요청 관계는?
  • Pool 의 설정 옵션 들은?
  • Pool 의 모니터링 은?

🎯 핵심 한 문장

Connection Pool 은 애플리케이션 시작 시 미리 N 개의 Connection 을 만들어 풀에 보관하고, 요청 시 빌려주고 사용 후 반환받아 재사용하는 구조로, Spring Boot 기본인 HikariCP 가 가장 빠르며 풀 크기는 CPU 코어 수와 작업 시간을 고려해 결정한다.
Connection Pool 은 "Connection 의 수영장" 으로, 미리 만들어둔 연결들을 보관하고 재사용하게 한다.
동작은 (1) 시작 시 N 개 미리 생성, (2) 요청 시 풀에서 빌림 (borrow), (3) 사용 후 풀에 반환 (return), (4) 풀의 Connection 은 계속 재사용 이다.
대표 구현체는 HikariCP (Spring Boot 기본, 가장 빠름), DBCP2 (Apache), Tomcat JDBC Pool (Tomcat 내장) 이며, HikariCP 가 단순·빠른 설계로 사실상 표준이다.
풀 크기는 너무 크면 DB 부담·메모리 낭비, 너무 작으면 대기 발생이라 CPU 코어 수 × 2 + 디스크 수 같은 공식이 가이드가 되고, 풀이 부족하면 요청은 대기 (connection timeout) 한다.

비유 — 도서관 인기 책 예비 비치

Connection Pool = 도서관 예비 비치:

매 요청 연결 = 매번 출판사 주문:
  - 주문→배송→읽기→반품
  - 비싼 비용 (Unit 4.1)

Connection Pool = 예비 비치:
  - 인기 책 10권 미리 (N개)
  - 손님 → 책 빌림 (borrow)
  - 읽고 반납 (return)
  - 다른 손님 또 빌림 (재사용)

동작:
  1. 도서관 개관 시 10권 준비 (시작 시)
  2. 빌리려는 사람 (요청)
  3. 책 있으면 빌려줌 (borrow)
  4. 책 없으면 대기 (timeout)
  5. 반납 (return → 풀에)
  6. 다음 손님 사용 (재사용)

풀 크기:
  - 너무 적음: 대기 길어짐
  - 너무 많음: 보관 공간 낭비 (메모리/DB)
  - 적절: CPU/작업 시간 기반

HikariCP = 가장 빠른 도서관:
  - 빌림/반납 효율적
  - Spring Boot 기본

→ Connection Pool = 미리 N 개 + 빌림/반환/재사용 (HikariCP 기본, 풀 크기 중요).


🧭 9개 섹션 로드맵

1. Connection Pool 정의
2. 풀 동작 (미리/빌림/반환/재사용)
3. HikariCP (Spring Boot 기본)
4. 풀 크기 결정
5. 풀 부족 시 대기
6. 대표 구현체 비교
7. 풀 크기 vs 동시 요청
8. 설정 옵션과 모니터링
9. 면접 + 자기 점검

1️⃣ Connection Pool 정의

1.1 정의

Connection Pool:

  미리 만들어둔 Connection 들의 모음.

  - "수영장 (Pool)"
  - 재사용
  - 비용 분산

1.2 핵심 아이디어

핵심 아이디어:

  연결 비싸다 (Unit 4.1):
    → 한 번 만들고
    → 여러 번 사용
    → 비용 1/N

1.3 위치

Pool 위치:

  애플리케이션 측:
    - JVM 안의 객체
    - DB 와는 N 개 연결

  → 클라이언트 측 풀

1.4 ILIC 의 맥락

Connection Pool (ILIC)

ILIC backend (Spring Boot):
  - HikariCP 자동 설정
  - 시작 시 풀 초기화
  - 미리 10개 (기본) Connection

  431 API:
    - 매 요청 풀에서 빌림
    - 사용 후 반환
    - 같은 Connection 여러 요청 재사용

  → DB 세션 10개로 수많은 요청 처리

1.5 자기 점검 답변

Connection Pool 의 정의는?

:
1. 정의:

  • 미리 만든 Connection 모음
  1. 핵심:

    • 재사용 (비용 분산)
  2. 위치:

    • 애플리케이션
  3. 목적:

    • 효율 + 안정

2️⃣ 풀 동작 (미리/빌림/반환/재사용)

2.1 4단계 동작

풀 동작 4단계:

1. 미리 생성 (init)
   - 시작 시 N 개

2. 빌림 (borrow / getConnection)
   - 요청 시 풀에서

3. 사용 (use)
   - SQL 실행

4. 반환 (return / close)
   - 풀로 돌려놓음

2.2 시각화

풀 동작:

[애플리케이션 시작]
   ↓
[풀 초기화] — N 개 Connection 생성
   ↓
[요청1] → [풀에서 Conn1 빌림] → [DB 작업] → [Conn1 반환]
[요청2] → [풀에서 Conn2 빌림] → [DB 작업] → [Conn2 반환]
[요청3] → [풀에서 Conn1 재사용] → [DB 작업] → [Conn1 반환]
   ...

→ 같은 Connection 무한 재사용

2.3 미리 생성

미리 생성 (init):

  애플리케이션 시작 시:
    - N 개 Connection 만듦
    - TCP/인증/세션 모두 (한 번만)
    - 풀에 보관

→ 시작 비용, 이후 빠름

2.4 빌림 (borrow)

// 빌림 (borrow)
DataSource ds = ...;
Connection c = ds.getConnection();
// 풀에서 idle Connection 하나 빌림
// 사용 가능한 게 있으면 즉시
// 없으면 대기 또는 timeout

2.5 사용

사용:

  빌린 Connection 으로:
    - SQL 실행
    - 결과 처리
    - 트랜잭션

  마치 새 연결처럼 사용

2.6 반환 (return)

// 반환 (close = 풀로 반환)
c.close();
// 실제로 TCP close 안 함
// 풀로 돌려놓음
// 다음 요청이 재사용

2.7 재사용

재사용:

  반환된 Connection:
    - 풀의 idle 상태
    - 다음 요청에 빌려줌
    - 무한 재사용

→ 1번 만들고 1000번 사용

2.8 ILIC 의 맥락

// 풀 동작 (ILIC)
@Repository
public class ShipmentDao {
    private final DataSource dataSource;   // HikariDataSource (풀)
    
    public ShipmentDao(DataSource ds) {
        this.dataSource = ds;
    }
    
    public Shipment get(Long id) throws Exception {
        // 1. 빌림 (borrow)
        try (Connection c = dataSource.getConnection()) {
            // 풀에서 빌린 Connection
            
            // 3. 사용
            try (PreparedStatement ps = c.prepareStatement(
                    "select * from shipments where id = ?")) {
                ps.setLong(1, id);
                try (ResultSet rs = ps.executeQuery()) {
                    if (rs.next()) {
                        Shipment s = new Shipment();
                        s.setId(rs.getLong("id"));
                        return s;
                    }
                }
            }
        }
        // 4. close → 풀로 반환 (try-with-resources)
        // → 다른 요청이 재사용
        return null;
    }
}
class Shipment { void setId(Long id) {} }

2.9 자기 점검 답변

풀 동작 (미리 N개, 빌림/반환/재사용) 은?

:
1. 4단계:

  • 미리/빌림/사용/반환
  1. 미리:

    • 시작 시 N 개
  2. 빌림/반환:

    • getConnection / close
  3. 재사용:

    • 무한 (풀 회전)

3️⃣ HikariCP (Spring Boot 기본)

3.1 HikariCP

HikariCP:

  "Hikari" (光, 빛, 일본어):
    - 가장 빠른 Connection Pool
    - 단순한 설계
    - Spring Boot 2+ 기본

3.2 빠른 이유

HikariCP 빠른 이유:

  - 바이트코드 레벨 최적화
  - 단순한 자료구조
  - Lock 최소화
  - "복잡함이 적이다" 철학

3.3 Spring Boot 기본

Spring Boot 기본:

  의존성:
    - spring-boot-starter-jdbc
    - spring-boot-starter-data-jpa
    → HikariCP 자동 포함

  자동 설정:
    - HikariDataSource 빈 자동
    - application.yml 로 설정

3.4 설정 예시

# application.yml (HikariCP)
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/ilic
    username: ilic_user
    password: pwd
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      maximum-pool-size: 20      # 최대 풀 크기
      minimum-idle: 5            # 최소 유지
      connection-timeout: 30000  # 빌림 대기 (ms)
      idle-timeout: 600000       # idle 정리 (ms)
      max-lifetime: 1800000      # 최대 수명 (ms)

3.5 ILIC 의 맥락

HikariCP (ILIC)

ILIC backend:
  - Spring Boot 3
  - spring-boot-starter-data-jpa 의존성
  - → HikariCP 자동 활성화

  application.yml:
    spring:
      datasource:
        url: jdbc:mysql://mysql:3306/ilic
        hikari:
          maximum-pool-size: 20
          minimum-idle: 5
          connection-timeout: 30000

  → HikariCP 가 10개 (기본) ~ 20개 (설정) 관리
  → 모든 431 API 가 공유
  → 가장 빠른 풀 (현대 표준)

3.6 자기 점검 답변

HikariCP 가 Spring Boot 기본인 이유는?

:
1. HikariCP:

  • 가장 빠른 풀
  1. 빠른 이유:

    • 바이트코드 최적화
    • 단순 설계
  2. Spring Boot:

    • 2+ 기본
  3. 설정:

    • application.yml

4️⃣ 풀 크기 결정

4.1 풀 크기 공식

풀 크기 가이드:

  pool_size = Tn × (Cm - 1) + 1
    - Tn: CPU 코어 수
    - Cm: 동시 작업 수

  단순화:
    pool_size = CPU 코어 × 2 + 디스크 수

  → 예: 8 코어 → 17 개

4.2 너무 큰 풀

너무 큰 풀:

  - DB 부담 (세션 ↑)
  - 메모리 낭비
  - 컨텍스트 스위칭 ↑
  - 오히려 느림

→ 큰 게 좋은 게 아님

4.3 너무 작은 풀

너무 작은 풀:

  - 대기 발생
  - 응답 지연
  - timeout

→ 적절히

4.4 HikariCP 권장

HikariCP 권장:

  HikariCP wiki:
    - "About Pool Sizing"
    - CPU 코어 × 2 + 디스크 수
    - 작게 시작, 모니터링

→ "fewer is better"

4.5 작업 시간 고려

작업 시간 고려:

  쿼리 짧음 (1ms):
    - 풀 작게도 OK

  쿼리 김 (100ms):
    - 풀 더 크게
    - 대기 줄이려고

→ Throughput 계산

4.6 ILIC 의 맥락

풀 크기 결정 (ILIC)

ILIC backend:
  - CPU 4 코어
  - DB 작업 짧음 (대부분 < 50ms)
  - 동시 요청 ~100

  공식: 4 × 2 + 1 (디스크) = 9
  실제: 10~20 정도 적절

  application.yml:
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5

  모니터링:
    - 활성 Connection 수
    - 대기 시간
    - timeout 횟수
    → 조정

4.7 자기 점검 답변

풀 크기 결정 (CPU 코어, 작업 시간) 은?

:
1. 공식:

  • CPU × 2 + 디스크
  1. 너무 큼:

    • DB 부담, 컨텍스트 스위칭
  2. 너무 작음:

    • 대기, timeout
  3. HikariCP:

    • fewer is better

5️⃣ 풀 부족 시 대기

5.1 풀 부족

풀 부족 시:

  동시 요청 > 풀 크기:
    - 남은 Connection 없음
    - 새 요청은 대기
    - timeout 까지

5.2 대기

대기 동작:

  getConnection() 호출:
    - 풀에 idle 있나?
      - YES: 즉시 빌림
      - NO: 대기
    - 다른 요청 반환 대기
    - connection-timeout 까지

5.3 Timeout

// connection-timeout (HikariCP 기본 30초)
try {
    Connection c = dataSource.getConnection();
    // 30초 안에 못 빌리면 예외
} catch (SQLTransientConnectionException e) {
    // "HikariPool-1 - Connection is not available, 
    //  request timed out after 30000ms"
}

5.4 큐잉

큐잉:

  대기 요청:
    - 큐에 줄 섬 (FIFO)
    - 반환되는 순서로
    - 공정한 분배

5.5 응답 지연

응답 지연:

  풀 부족 → 대기 → 응답 지연:
    - 사용자 경험 ↓
    - 누적 지연
    - 시스템 부하 ↑

→ 풀 크기 + 쿼리 최적화

5.6 ILIC 의 맥락

풀 부족 시나리오 (ILIC)

ILIC pool = 10, 동시 요청 50:
  - 10 요청: 즉시 빌림
  - 40 요청: 대기

  대기 시간:
    - 다른 요청이 close 할 때까지
    - 쿼리 평균 50ms 라면
    - 대기 ~200ms (큐 깊이)

  대기 > 30초:
    - SQLTransientConnectionException
    - 503 응답

  해결:
    - 풀 크기 ↑ (20~30)
    - 쿼리 최적화 (짧게)
    - DB 부하 분산

5.7 자기 점검 답변

풀 크기보다 동시 요청이 많으면?

:
1. 부족:

  • 남은 X
  1. 대기:

    • timeout 까지
  2. timeout:

    • SQLTransientException
  3. 해결:

    • 풀 ↑, 쿼리 최적화

6️⃣ 대표 구현체 비교

6.1 세 구현체

구현체특징사용처
HikariCP가장 빠름, 단순Spring Boot 기본
DBCP2Apache, 기능 풍부레거시
Tomcat JDBC PoolTomcat 내장Tomcat 환경

6.2 HikariCP

HikariCP:

  - 단순함이 강점
  - 바이트코드 최적화
  - 가장 빠름
  - Spring Boot 2+ 기본

→ 현대 표준

6.3 DBCP2

DBCP2 (Apache Commons DBCP):

  - Apache 프로젝트
  - 기능 풍부
  - 설정 옵션 많음
  - 레거시에 많음

→ 안정적이나 HikariCP 보다 느림

6.4 Tomcat JDBC Pool

Tomcat JDBC Pool:

  - Tomcat 7+ 내장
  - 외장 톰캣 환경
  - DBCP 대안으로 등장

→ Spring Boot 등장 전 인기

6.5 성능 비교

성능 비교 (대략):

  HikariCP: 100 (기준)
  Tomcat JDBC Pool: 90
  DBCP2: 70~80

  (벤치마크 결과, HikariCP 빠름)

6.6 ILIC 의 맥락

구현체 선택 (ILIC)

ILIC = HikariCP:
  - Spring Boot 3 기본
  - 가장 빠름
  - 별도 설정 없이

  build.gradle:
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    # → HikariCP 자동 포함

  다른 풀로 바꾸려면:
    - exclude HikariCP
    - 다른 풀 의존성 추가
    → 거의 안 함 (HikariCP 가 최선)

→ 현대 Spring 은 거의 HikariCP

6.7 자기 점검 답변

대표 구현체 (HikariCP / DBCP2 / Tomcat JDBC Pool) 는?

:
1. HikariCP:

  • 가장 빠름, 기본
  1. DBCP2:

    • Apache, 레거시
  2. Tomcat JDBC:

    • Tomcat 내장
  3. 선택:

    • HikariCP 표준

7️⃣ 풀 크기 vs 동시 요청

7.1 관계

풀 크기 vs 동시 요청:

  동시 요청 ≤ 풀 크기:
    - 즉시 처리 (대기 X)

  동시 요청 > 풀 크기:
    - 일부 대기
    - 큐잉

7.2 최적 비율

최적 비율:

  풀 크기 = 평균 동시 활성 요청:
    - 너무 크면 낭비
    - 너무 작으면 대기

→ 모니터링 후 조정

7.3 짧은 쿼리

짧은 쿼리:

  대부분 쿼리 짧음 (1~10ms):
    - 풀 회전 빠름
    - 작은 풀로도 많은 요청
    - 큐잉 적음

7.4 긴 쿼리/트랜잭션

긴 쿼리/트랜잭션:

  Connection 점유 김:
    - 풀 회전 느림
    - 큰 풀 필요
    - 또는 쿼리 최적화

7.5 Throughput 계산

Throughput (대략):

  Throughput = 풀 크기 / 평균 작업 시간
  
  예: 풀 10, 쿼리 평균 10ms
    = 10 / 0.01 초 = 1000 req/sec

→ 풀 크기로 처리량 추정

7.6 ILIC 의 맥락

풀 크기 vs 동시 요청 (ILIC)

ILIC 시나리오:
  - 동시 활성 요청: ~50
  - 쿼리 평균: 30ms
  - 풀 크기: 20

  처리량:
    - 20 / 0.03 = ~666 req/sec
  
  하지만 동시 50:
    - 20 즉시 처리
    - 30 대기
    - 30ms × (30/20) = ~45ms 대기

  조정:
    - 풀 → 30 (대기 ↓)
    - 또는 쿼리 캐싱 (작업 시간 ↓)
    → 모니터링으로 결정

7.7 자기 점검 답변

풀 크기 vs 동시 요청 관계는?

:
1. 관계:

  • 동시 ≤ 풀 → 즉시
  • 풀 → 대기

  1. 최적:

    • 평균 활성 요청
  2. Throughput:

    • 풀 / 작업 시간
  3. 조정:

    • 모니터링

8️⃣ 설정 옵션과 모니터링

8.1 주요 옵션

HikariCP 주요 옵션:

  - maximum-pool-size: 최대 (기본 10)
  - minimum-idle: 최소 idle (기본 = max)
  - connection-timeout: 빌림 대기 (30초)
  - idle-timeout: idle 정리 (10분)
  - max-lifetime: 최대 수명 (30분)
  - leak-detection-threshold: 누수 감지

8.2 max-lifetime

max-lifetime:

  Connection 최대 수명:
    - 일정 시간 후 교체
    - DB 측 wait_timeout 보다 짧게
    - 끊긴 Connection 회피

  ex: 30분

8.3 idle-timeout

idle-timeout:

  사용 안 하는 Connection:
    - 일정 시간 후 정리
    - 최소 idle 까지만
    - 메모리 효율

  ex: 10분

8.4 leak-detection-threshold

leak-detection-threshold:

  Connection 누수 감지:
    - 빌리고 N 초 안에 반환 X
    - 경고 로그
    - 누수 찾기

  ex: 60000 (60초)

8.5 모니터링

모니터링:

  HikariCP MBean:
    - active connections
    - idle connections
    - waiting threads
    - total connections

  Spring Boot Actuator:
    - /actuator/metrics
    - hikaricp.connections.*

→ 운영 중 관찰

8.6 ILIC 의 맥락

# ILIC application.yml (HikariCP 설정)
spring:
  datasource:
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 30000        # 30초
      idle-timeout: 600000             # 10분
      max-lifetime: 1800000            # 30분
      leak-detection-threshold: 60000  # 60초 (누수 감지)
      
      # MySQL wait_timeout (28800 = 8시간) 보다 max-lifetime 짧게

# 모니터링 (Actuator)
management:
  endpoints:
    web:
      exposure:
        include: health,metrics,prometheus

# Prometheus + Grafana 로 시각화 가능
# - hikaricp_connections_active
# - hikaricp_connections_idle
# - hikaricp_connections_pending

8.7 자기 점검 답변

Pool 의 설정 옵션과 모니터링은?

:
1. 옵션:

  • max-pool, min-idle, timeout
  1. max-lifetime:

    • 최대 수명
  2. leak-detection:

    • 누수 감지
  3. 모니터링:

    • MBean, Actuator

9️⃣ 면접 + 자기 점검

9.1 면접 단골 질문 매핑

Q핵심 답변
Connection Pool?미리 N개, 재사용
동작?미리/빌림/반환/재사용
HikariCP?Spring Boot 기본, 가장 빠름
풀 크기?CPU × 2 + 디스크
부족 시?대기, timeout
대표 구현체?HikariCP/DBCP2/Tomcat
풀 크기 vs 요청?같거나 ≤
Throughput?풀 / 작업 시간
max-lifetime?최대 수명
누수 감지?leak-detection

9.2 자기 점검 체크리스트

정의

  • Pool

동작

  • 4단계

HikariCP

  • 기본/빠름

풀 크기

  • 공식

부족

  • 대기

구현체

  • 3개

풀 vs 요청

  • 관계

설정

  • 옵션

9.3 추가 심화 질문

Q1: prepared statement 캐싱?

답:

  • PreparedStatement 재사용
  • Connection 단위
  • 풀과 함께 효과
  • HikariCP 옵션

Q2: read replica + 풀?

답:

  • 읽기 전용 DB 별도
  • 읽기 풀 + 쓰기 풀 분리
  • 부하 분산
  • AbstractRoutingDataSource

Q3: 풀 vs 비동기 DB?

답:

  • 풀: 동기 (스레드별)
  • 비동기 (R2DBC): 적은 스레드
  • 더 높은 동시성
  • 트레이드오프 (복잡도)

Q4: 풀 워밍업?

답:

  • 시작 시 최소 idle 미리
  • 첫 요청 빠르게
  • minimum-idle = maximum-pool-size

Q5: 멀티 DataSource?

답:

  • 여러 DB 연결
  • 각각 풀
  • @Primary, @Qualifier
  • 트랜잭션 관리 주의

🎯 핵심 요약 — 3줄 정리

1. Connection Pool 동작

  • 미리 N 개 생성 → 빌림 → 사용 → 반환 → 재사용 (무한 회전)
  • close() 는 실제 close 가 아닌 풀로 반환

2. HikariCP (Spring Boot 기본)

  • 가장 빠른 풀 (바이트코드 최적화, 단순 설계)
  • application.yml 로 설정 (max-pool-size, timeout 등)

3. 풀 크기와 대기

  • CPU 코어 × 2 + 디스크 수 가이드
  • 풀 부족 시 connection-timeout 까지 대기 (못 빌리면 예외)

📚 다음으로...

Unit 4.3 — DB 세션과 연결 구조

이번 Unit에서 Connection Pool 을 봤다면, 다음은 DB 세션과 연결 구조.

  • 연결 절차 (TCP/세션/SQL/종료)
  • 풀 N 개 = DB 세션 N 개
  • 한 Connection 동시 두 트랜잭션 불가

Phase 4 진행 상황

🔗 Phase 4 — Connection Pool과 DB 세션
  ✅ Unit 4.1 매 요청마다 연결의 비효율
  ✅ Unit 4.2 Connection Pool 개념 ★깊이 ← 여기
  ⏭ Unit 4.3 DB 세션과 연결 구조
  ⏭ Unit 4.4 DB Lock 개념

6주차 누적 진행

🧪 Part A (9 Unit) ✅
💾 Part B — DB 접근의 진화
  ✅ Phase 3 — JDBC (3)
  🔗 Phase 4 — Connection Pool (2/4)

총: 14/28 Unit (절반!)

★ 깊이 파기 — Connection Pool 완료

profile
Software Developer

0개의 댓글