F-LAB JAVA · 6주차 · Phase 4 · Connection Pool과 DB 세션
★ 깊이 파기 — Connection Pool 의 모든 것
이 Unit을 끝내면 다음을 답할 수 있어야 한다.
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 기본, 풀 크기 중요).
1. Connection Pool 정의
2. 풀 동작 (미리/빌림/반환/재사용)
3. HikariCP (Spring Boot 기본)
4. 풀 크기 결정
5. 풀 부족 시 대기
6. 대표 구현체 비교
7. 풀 크기 vs 동시 요청
8. 설정 옵션과 모니터링
9. 면접 + 자기 점검
Connection Pool:
미리 만들어둔 Connection 들의 모음.
- "수영장 (Pool)"
- 재사용
- 비용 분산
핵심 아이디어:
연결 비싸다 (Unit 4.1):
→ 한 번 만들고
→ 여러 번 사용
→ 비용 1/N
Pool 위치:
애플리케이션 측:
- JVM 안의 객체
- DB 와는 N 개 연결
→ 클라이언트 측 풀
Connection Pool (ILIC)
ILIC backend (Spring Boot):
- HikariCP 자동 설정
- 시작 시 풀 초기화
- 미리 10개 (기본) Connection
431 API:
- 매 요청 풀에서 빌림
- 사용 후 반환
- 같은 Connection 여러 요청 재사용
→ DB 세션 10개로 수많은 요청 처리
Connection Pool 의 정의는?
답:
1. 정의:
핵심:
위치:
목적:
풀 동작 4단계:
1. 미리 생성 (init)
- 시작 시 N 개
2. 빌림 (borrow / getConnection)
- 요청 시 풀에서
3. 사용 (use)
- SQL 실행
4. 반환 (return / close)
- 풀로 돌려놓음
풀 동작:
[애플리케이션 시작]
↓
[풀 초기화] — N 개 Connection 생성
↓
[요청1] → [풀에서 Conn1 빌림] → [DB 작업] → [Conn1 반환]
[요청2] → [풀에서 Conn2 빌림] → [DB 작업] → [Conn2 반환]
[요청3] → [풀에서 Conn1 재사용] → [DB 작업] → [Conn1 반환]
...
→ 같은 Connection 무한 재사용
미리 생성 (init):
애플리케이션 시작 시:
- N 개 Connection 만듦
- TCP/인증/세션 모두 (한 번만)
- 풀에 보관
→ 시작 비용, 이후 빠름
// 빌림 (borrow)
DataSource ds = ...;
Connection c = ds.getConnection();
// 풀에서 idle Connection 하나 빌림
// 사용 가능한 게 있으면 즉시
// 없으면 대기 또는 timeout
사용:
빌린 Connection 으로:
- SQL 실행
- 결과 처리
- 트랜잭션
마치 새 연결처럼 사용
// 반환 (close = 풀로 반환)
c.close();
// 실제로 TCP close 안 함
// 풀로 돌려놓음
// 다음 요청이 재사용
재사용:
반환된 Connection:
- 풀의 idle 상태
- 다음 요청에 빌려줌
- 무한 재사용
→ 1번 만들고 1000번 사용
// 풀 동작 (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) {} }
풀 동작 (미리 N개, 빌림/반환/재사용) 은?
답:
1. 4단계:
미리:
빌림/반환:
재사용:
HikariCP:
"Hikari" (光, 빛, 일본어):
- 가장 빠른 Connection Pool
- 단순한 설계
- Spring Boot 2+ 기본
HikariCP 빠른 이유:
- 바이트코드 레벨 최적화
- 단순한 자료구조
- Lock 최소화
- "복잡함이 적이다" 철학
Spring Boot 기본:
의존성:
- spring-boot-starter-jdbc
- spring-boot-starter-data-jpa
→ HikariCP 자동 포함
자동 설정:
- HikariDataSource 빈 자동
- application.yml 로 설정
# 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)
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 가 공유
→ 가장 빠른 풀 (현대 표준)
HikariCP 가 Spring Boot 기본인 이유는?
답:
1. HikariCP:
빠른 이유:
Spring Boot:
설정:
풀 크기 가이드:
pool_size = Tn × (Cm - 1) + 1
- Tn: CPU 코어 수
- Cm: 동시 작업 수
단순화:
pool_size = CPU 코어 × 2 + 디스크 수
→ 예: 8 코어 → 17 개
너무 큰 풀:
- DB 부담 (세션 ↑)
- 메모리 낭비
- 컨텍스트 스위칭 ↑
- 오히려 느림
→ 큰 게 좋은 게 아님
너무 작은 풀:
- 대기 발생
- 응답 지연
- timeout
→ 적절히
HikariCP 권장:
HikariCP wiki:
- "About Pool Sizing"
- CPU 코어 × 2 + 디스크 수
- 작게 시작, 모니터링
→ "fewer is better"
작업 시간 고려:
쿼리 짧음 (1ms):
- 풀 작게도 OK
쿼리 김 (100ms):
- 풀 더 크게
- 대기 줄이려고
→ Throughput 계산
풀 크기 결정 (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 횟수
→ 조정
풀 크기 결정 (CPU 코어, 작업 시간) 은?
답:
1. 공식:
너무 큼:
너무 작음:
HikariCP:
풀 부족 시:
동시 요청 > 풀 크기:
- 남은 Connection 없음
- 새 요청은 대기
- timeout 까지
대기 동작:
getConnection() 호출:
- 풀에 idle 있나?
- YES: 즉시 빌림
- NO: 대기
- 다른 요청 반환 대기
- connection-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"
}
큐잉:
대기 요청:
- 큐에 줄 섬 (FIFO)
- 반환되는 순서로
- 공정한 분배
응답 지연:
풀 부족 → 대기 → 응답 지연:
- 사용자 경험 ↓
- 누적 지연
- 시스템 부하 ↑
→ 풀 크기 + 쿼리 최적화
풀 부족 시나리오 (ILIC)
ILIC pool = 10, 동시 요청 50:
- 10 요청: 즉시 빌림
- 40 요청: 대기
대기 시간:
- 다른 요청이 close 할 때까지
- 쿼리 평균 50ms 라면
- 대기 ~200ms (큐 깊이)
대기 > 30초:
- SQLTransientConnectionException
- 503 응답
해결:
- 풀 크기 ↑ (20~30)
- 쿼리 최적화 (짧게)
- DB 부하 분산
풀 크기보다 동시 요청이 많으면?
답:
1. 부족:
대기:
timeout:
해결:
| 구현체 | 특징 | 사용처 |
|---|---|---|
| HikariCP | 가장 빠름, 단순 | Spring Boot 기본 |
| DBCP2 | Apache, 기능 풍부 | 레거시 |
| Tomcat JDBC Pool | Tomcat 내장 | Tomcat 환경 |
HikariCP:
- 단순함이 강점
- 바이트코드 최적화
- 가장 빠름
- Spring Boot 2+ 기본
→ 현대 표준
DBCP2 (Apache Commons DBCP):
- Apache 프로젝트
- 기능 풍부
- 설정 옵션 많음
- 레거시에 많음
→ 안정적이나 HikariCP 보다 느림
Tomcat JDBC Pool:
- Tomcat 7+ 내장
- 외장 톰캣 환경
- DBCP 대안으로 등장
→ Spring Boot 등장 전 인기
성능 비교 (대략):
HikariCP: 100 (기준)
Tomcat JDBC Pool: 90
DBCP2: 70~80
(벤치마크 결과, HikariCP 빠름)
구현체 선택 (ILIC)
ILIC = HikariCP:
- Spring Boot 3 기본
- 가장 빠름
- 별도 설정 없이
build.gradle:
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
# → HikariCP 자동 포함
다른 풀로 바꾸려면:
- exclude HikariCP
- 다른 풀 의존성 추가
→ 거의 안 함 (HikariCP 가 최선)
→ 현대 Spring 은 거의 HikariCP
대표 구현체 (HikariCP / DBCP2 / Tomcat JDBC Pool) 는?
답:
1. HikariCP:
DBCP2:
Tomcat JDBC:
선택:
풀 크기 vs 동시 요청:
동시 요청 ≤ 풀 크기:
- 즉시 처리 (대기 X)
동시 요청 > 풀 크기:
- 일부 대기
- 큐잉
최적 비율:
풀 크기 = 평균 동시 활성 요청:
- 너무 크면 낭비
- 너무 작으면 대기
→ 모니터링 후 조정
짧은 쿼리:
대부분 쿼리 짧음 (1~10ms):
- 풀 회전 빠름
- 작은 풀로도 많은 요청
- 큐잉 적음
긴 쿼리/트랜잭션:
Connection 점유 김:
- 풀 회전 느림
- 큰 풀 필요
- 또는 쿼리 최적화
Throughput (대략):
Throughput = 풀 크기 / 평균 작업 시간
예: 풀 10, 쿼리 평균 10ms
= 10 / 0.01 초 = 1000 req/sec
→ 풀 크기로 처리량 추정
풀 크기 vs 동시 요청 (ILIC)
ILIC 시나리오:
- 동시 활성 요청: ~50
- 쿼리 평균: 30ms
- 풀 크기: 20
처리량:
- 20 / 0.03 = ~666 req/sec
하지만 동시 50:
- 20 즉시 처리
- 30 대기
- 30ms × (30/20) = ~45ms 대기
조정:
- 풀 → 30 (대기 ↓)
- 또는 쿼리 캐싱 (작업 시간 ↓)
→ 모니터링으로 결정
풀 크기 vs 동시 요청 관계는?
답:
1. 관계:
풀 → 대기
최적:
Throughput:
조정:
HikariCP 주요 옵션:
- maximum-pool-size: 최대 (기본 10)
- minimum-idle: 최소 idle (기본 = max)
- connection-timeout: 빌림 대기 (30초)
- idle-timeout: idle 정리 (10분)
- max-lifetime: 최대 수명 (30분)
- leak-detection-threshold: 누수 감지
max-lifetime:
Connection 최대 수명:
- 일정 시간 후 교체
- DB 측 wait_timeout 보다 짧게
- 끊긴 Connection 회피
ex: 30분
idle-timeout:
사용 안 하는 Connection:
- 일정 시간 후 정리
- 최소 idle 까지만
- 메모리 효율
ex: 10분
leak-detection-threshold:
Connection 누수 감지:
- 빌리고 N 초 안에 반환 X
- 경고 로그
- 누수 찾기
ex: 60000 (60초)
모니터링:
HikariCP MBean:
- active connections
- idle connections
- waiting threads
- total connections
Spring Boot Actuator:
- /actuator/metrics
- hikaricp.connections.*
→ 운영 중 관찰
# 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
Pool 의 설정 옵션과 모니터링은?
답:
1. 옵션:
max-lifetime:
leak-detection:
모니터링:
| Q | 핵심 답변 |
|---|---|
| Connection Pool? | 미리 N개, 재사용 |
| 동작? | 미리/빌림/반환/재사용 |
| HikariCP? | Spring Boot 기본, 가장 빠름 |
| 풀 크기? | CPU × 2 + 디스크 |
| 부족 시? | 대기, timeout |
| 대표 구현체? | HikariCP/DBCP2/Tomcat |
| 풀 크기 vs 요청? | 같거나 ≤ |
| Throughput? | 풀 / 작업 시간 |
| max-lifetime? | 최대 수명 |
| 누수 감지? | leak-detection |
답:
답:
답:
답:
답:
1. Connection Pool 동작
2. HikariCP (Spring Boot 기본)
3. 풀 크기와 대기
이번 Unit에서 Connection Pool 을 봤다면, 다음은 DB 세션과 연결 구조.
🔗 Phase 4 — Connection Pool과 DB 세션
✅ Unit 4.1 매 요청마다 연결의 비효율
✅ Unit 4.2 Connection Pool 개념 ★깊이 ← 여기
⏭ Unit 4.3 DB 세션과 연결 구조
⏭ Unit 4.4 DB Lock 개념
🧪 Part A (9 Unit) ✅
💾 Part B — DB 접근의 진화
✅ Phase 3 — JDBC (3)
🔗 Phase 4 — Connection Pool (2/4)
총: 14/28 Unit (절반!)
★ 깊이 파기 — Connection Pool 완료