커넥션 관리
개발 환경에서는 잘 동작하던 DB가 운영 환경에서 불안정해지는 이유 중 하나가 커넥션 관리입니다. 데이터베이스 커넥션은 네트워크, 인증, 세션 상태, 서버 메모리를 사용하는 리소스이므로, 요청마다 새로 만들기보다 풀링과 제한을 통해 관리해야 합니다.
커넥션의 생성 과정
데이터베이스 커넥션을 생성하려면 TCP 연결, TLS 협상(사용 시), DB 프로토콜 핸드셰이크, 인증, 세션 초기화 과정을 거칩니다. 이 비용은 요청 처리 시간뿐 아니라 DB 서버의 메모리와 프로세스/스레드 자원에도 영향을 줍니다.
커넥션 풀의 개념
커넥션 풀은 커넥션을 재사용하는 기법입니다. 매 요청마다 커넥션을 생성/종료하는 대신, 풀에서 빌리고 반환합니다. 핵심은 “많이 열어두기”가 아니라 DB가 감당 가능한 수 안에서 대기와 처리량을 균형 있게 맞추는 것입니다.
커넥션 풀 구조
HikariCP — 대표적인 JDBC 커넥션 풀
Spring Boot 2.0+에서 기본으로 쓰이는 JDBC 커넥션 풀입니다. 설정 기본값이 보수적으로 잡혀 있지만, 운영에서는 앱 인스턴스 수, DB max_connections, 쿼리 지연 시간, 트랜잭션 길이를 함께 보고 조정해야 합니다.
| 파라미터 | 설명 | 기본값 | 운영 기준 |
|---|---|---|---|
| minimumIdle | 최소 유휴 커넥션 | maximumPoolSize | 고정 크기 풀은 미설정 또는 maximumPoolSize와 동일 |
| maximumPoolSize | 최대 커넥션 수 | 10 | 앱 인스턴스 수 × 풀 크기 ≤ DB 허용 연결 수 안에서 부하 테스트 |
| connectionTimeout | 커넥션 획득 대기 | 30초 | 사용자 요청 SLA보다 짧게, 보통 빠르게 실패하도록 조정 |
| idleTimeout | 유휴 커넥션 유지 | 600초 | minimumIdle < maximumPoolSize일 때 의미가 큼 |
| maxLifetime | 커넥션 최대 수명 | 1800초 | DB/방화벽/프록시 idle 제한보다 짧게 |
| keepaliveTime | 유휴 커넥션 keepalive | 120초 | maxLifetime보다 짧게, 네트워크 idle 종료가 있을 때 검토 |
| validationTimeout | 커넥션 유효성 검사 | 5초 | 장애 감지 지연과 false positive를 함께 고려 |
| leakDetectionThreshold | 리크 감지 임계값 | 0 (비활성) | 운영 상시 설정보다 장애 분석/스테이징에서 우선 활용 |
spring:
datasource:
hikari:
minimum-idle: 10
maximum-pool-size: 10
connection-timeout: 3000
idle-timeout: 600000
max-lifetime: 1740000
keepalive-time: 120000
leak-detection-threshold: 60000minimumIdle vs maximumPoolSize
다른 커넥션 풀 비교
| 풀/프록시 | 특징 | 환경 |
|---|---|---|
| HikariCP | Spring Boot 기본 JDBC 풀, 설정 단순 | Java |
| Apache DBCP2 | 오래된 Java 프로젝트에서 자주 사용 | Java |
| Tomcat Pool | Tomcat 내장 JDBC 풀 | Java/Tomcat |
| Oracle UCP | Oracle JDBC/UCP, RAC/FCF 같은 기능과 연계 | Oracle |
| c3p0 | 레거시 프로젝트에서 발견됨 | Java |
| pgBouncer | PostgreSQL 앞단의 경량 외부 풀러 | PostgreSQL |
| ProxySQL | MySQL 앞단 프록시, 라우팅/풀링/HA 보조 | MySQL |
Oracle UCP (Universal Connection Pool)
Oracle은 자체 커넥션 풀인 UCP를 제공합니다. RAC, Active Data Guard 같은 고가용성 구성에서는 Fast Connection Failover(FCF)가 FAN/ONS 이벤트를 이용해 죽은 커넥션을 감지하고 풀에서 제거하는 데 도움을 줍니다. 다만 이미 진행 중이던 트랜잭션은 예외를 받을 수 있으므로 애플리케이션 재시도 설계가 함께 필요합니다.
oracle.ucp.minPoolSize=5
oracle.ucp.maxPoolSize=20
oracle.ucp.initialPoolSize=10
oracle.ucp.connectionWaitTimeout=3
oracle.ucp.inactiveConnectionTimeout=300
RAC/HA 환경 추가 설정:
oracle.ucp.FastConnectionFailoverEnabled=true
→ FAN/ONS 이벤트로 장애 커넥션을 빠르게 감지·제거
→ 진행 중 트랜잭션은 예외 후 재시도 경로 필요pgBouncer (PostgreSQL 외부 풀)
Oracle processes, sessions 파라미터
Oracle 서버 측에서도 프로세스와 세션 수를 제한합니다. 이 값은 버전, 코어 수, CDB/PDB 구성, shared server 사용 여부에 따라 달라질 수 있으므로 현재 값을 확인한 뒤 산정해야 합니다.
-- 현재 설정 확인
SHOW PARAMETER processes;
SHOW PARAMETER sessions;
-- 현재 활성 세션 수 확인
SELECT COUNT(*) FROM v$session WHERE status = 'ACTIVE';
-- 전체 세션 현황
SELECT username, status, COUNT(*)
FROM v$session
WHERE username IS NOT NULL
GROUP BY username, status
ORDER BY COUNT(*) DESC;
-- 변경 (재시작 필요)
ALTER SYSTEM SET processes = 500 SCOPE = SPFILE;
-- sessions는 자동 계산: (1.5 * processes) + 22| 파라미터 | 기본값/산정 방식 | 설명 |
|---|---|---|
| processes | 환경에 따라 산정 | 동시에 접속 가능한 OS 사용자 프로세스 한계 |
| sessions | 기본 산식: (1.5 × processes) + 22 | 동시에 생성 가능한 세션 수 |
-- 현재 설정 확인
SHOW VARIABLES LIKE 'max_connections'; -- MySQL 기본값은 보통 151
SHOW STATUS LIKE 'Threads_connected'; -- 현재 연결 수
SHOW STATUS LIKE 'Max_used_connections'; -- 최대 동시 연결 수
-- 변경
SET GLOBAL max_connections = 500;
-- 영구 적용은 설정 파일 또는 SET PERSIST로 관리
-- 관리자/복제/모니터링용 예비 연결도 별도로 남겨 둠
-- wait_timeout: 유휴 커넥션 자동 종료 시간
SHOW VARIABLES LIKE 'wait_timeout'; -- MySQL 기본값은 보통 28800초(8시간)
SET GLOBAL wait_timeout = 600; -- 10분Connection Leak 진단
커넥션을 빌려가서 반환하지 않으면 커넥션 리크(Connection Leak)가 발생합니다. 풀이 고갈되면 새 요청은 커넥션을 기다리다가 connectionTimeout에 걸리고, 원인은 리크뿐 아니라 긴 트랜잭션, 느린 쿼리, 외부 API 대기일 수도 있습니다.
| 증상 | 원인 |
|---|---|
| 커넥션 타임아웃 빈발 | close() 호출 누락 |
| 활성 커넥션 수가 계속 증가 | try-with-resources 미사용 |
| 서버 재시작하면 일시적 해소 | 예외 발생 시 finally에서 반환 안 함 |
| 특정 기능 사용 후 발생 | 해당 코드에서 커넥션 미반환 |
// [나쁜 예] 예외 발생 시 커넥션 미반환
Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.executeQuery(); // 여기서 예외 발생하면?
conn.close(); // 이 줄이 실행되지 않음 → 리크 발생
// [좋은 예] try-with-resources로 자동 반환
try (Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
// 결과 처리
}
} // 자동으로 rs, pstmt, conn.close() → 풀에 반환
// 예외가 발생해도 반드시 반환됨커넥션 풀 모니터링
커넥션 유효성 검사
네트워크 장애, DB 재시작 등으로 풀 안의 커넥션이 끊어져 있을 수 있습니다. 유효성 검사로 죽은 커넥션을 미리 제거합니다.
Oracle Shared Server vs Dedicated Server
Oracle은 Dedicated Server와 Shared Server 방식을 제공합니다. Dedicated Server는 클라이언트마다 서버 프로세스가 배정되는 기본적인 형태이고, Shared Server는 많은 동시 연결을 더 적은 서버 프로세스로 처리하도록 설계된 구조입니다.
클라이언트 1 ──→ 서버 프로세스 1
클라이언트 2 ──→ 서버 프로세스 2
클라이언트 3 ──→ 서버 프로세스 3
장점: 각 클라이언트에 전용 프로세스 → 세션 상태와 실행 흐름이 단순
단점: 커넥션당 1개 프로세스 → 메모리 소비 큼
적합: 커넥션 수가 적고 응답 시간이 중요한 OLTP-- 현재 모드 확인
SELECT server FROM v$session WHERE sid = SYS_CONTEXT('USERENV', 'SID');
-- Shared Server 설정 (init 파라미터)
SHARED_SERVERS = 5 -- 공유 서버 프로세스 수
MAX_SHARED_SERVERS = 20 -- 최대 공유 서버 수
DISPATCHERS = '(PROTOCOL=TCP)(DISPATCHERS=2)'컨테이너/클라우드 환경의 커넥션 관리
커넥션 풀 트러블슈팅
문제 1: "Cannot acquire connection from pool"
원인: 풀 고갈 (리크 또는 풀 크기 부족)
진단: ActiveConnections == maximumPoolSize인지 확인
해결: leakDetectionThreshold 일시 활성화, Slow Query 확인
문제 2: "Connection is not available, request timed out"
원인: connectionTimeout 내에 커넥션 획득 실패
진단: PendingThreads 수 확인
해결: 리크/느린 쿼리/긴 트랜잭션 확인 후 풀 크기 또는 timeout 조정
문제 3: "Connection reset by peer"
원인: 방화벽/DB가 유휴 커넥션 끊음
진단: maxLifetime/keepaliveTime이 DB 또는 네트워크 idle 제한과 맞는지 확인
해결: maxLifetime을 제한보다 짧게, 필요 시 keepaliveTime 설정
문제 4: "Too many connections" (MySQL)
원인: 모든 앱서버 풀 합 > max_connections
진단: SHOW STATUS LIKE 'Max_used_connections';
해결: 풀 크기 축소, max_connections 증가, pgBouncer 도입
문제 5: "ORA-12519: TNS: no appropriate service handler found"
원인: Oracle processes/sessions 한계 초과
진단: SELECT COUNT(*) FROM v$session;
해결: processes 파라미터 증가, Shared Server 검토
문제 6: 간헐적 쿼리 실패
원인: 풀 안의 커넥션이 DB 재시작으로 끊어짐
진단: 앱 로그에서 "Broken pipe" 확인
해결: validationTimeout 설정, maxLifetime 단축정리
커넥션 풀 설정의 핵심은 적절한 크기, 짧고 안전한 점유 시간, 리크 방지입니다. 풀이 너무 작으면 대기가 발생하고, 너무 크면 DB 메모리와 스케줄링 비용이 커집니다. 공식 하나로 결정하기보다 앱 인스턴스 수, DB 연결 한계, 평균 쿼리 시간, 트랜잭션 길이를 측정해 부하 테스트로 조정해야 합니다.
모든 커넥션은 try-with-resources 또는 프레임워크의 트랜잭션 경계로 반드시 반환하고, 필요할 때 leakDetectionThreshold로 의심 구간을 찾아야 합니다. 여러 앱 서버의 풀 크기 합이 DB의 연결 한계와 운영 여유분을 초과하지 않도록 전체 아키텍처 관점에서 관리하는 것이 중요합니다.
시험에서는 커넥션 풀의 개념과 필요성, 주요 설정 파라미터(maximumPoolSize, connectionTimeout, maxLifetime), 커넥션 리크의 원인과 방지 방법이 자주 출제됩니다.
다음 절에서는 모니터링과 성능 진단을 다루겠습니다.