스레드 풀 & 커넥션 풀 튜닝 — 부하테스트 병목 해결
부하테스트에서 VU를 올릴수록 응답 시간이 갑자기 치솟는다면 풀(Pool) 고갈이 원인인 경우가 많습니다.
풀(Pool)이 필요한 이유
섹션 제목: “풀(Pool)이 필요한 이유”스레드와 DB 커넥션을 요청마다 새로 생성하면:
스레드 생성 비용: ~1msDB 커넥션 생성: ~5~50ms (TCP 핸드셰이크 + 인증)
초당 1,000 요청이면:→ 커넥션 생성에만 초당 5~50초 소비 → 불가능풀은 미리 만들어 둔 자원을 재사용하여 이 비용을 제거합니다.
스레드 풀 튜닝
섹션 제목: “스레드 풀 튜닝”Tomcat (Spring Boot)
섹션 제목: “Tomcat (Spring Boot)”server: tomcat: threads: max: 200 # 최대 스레드 수 (기본값: 200) min-spare: 20 # 대기 스레드 최솟값 accept-count: 100 # 대기 큐 크기 (스레드 소진 시 대기) connection-timeout: 5000 # 5초적정 스레드 수 계산:
스레드 수 = 목표 TPS × 평균 응답시간(초)
예시: 목표 500 TPS, 평균 응답 200ms→ 500 × 0.2 = 100 스레드여유분 20% 추가 → 120 스레드Node.js (단일 스레드 + Worker Threads)
섹션 제목: “Node.js (단일 스레드 + Worker Threads)”Node.js는 이벤트 루프 기반이라 스레드 풀 개념이 다릅니다.
const { Worker, isMainThread, workerData } = require('worker_threads');
// CPU 집중 작업만 Worker로 오프로드if (isMainThread) { // libuv 스레드 풀 크기 설정 (파일 I/O, crypto 등) process.env.UV_THREADPOOL_SIZE = 16; // 기본값: 4
function runHeavyTask(data) { return new Promise((resolve, reject) => { const worker = new Worker(__filename, { workerData: data }); worker.on('message', resolve); worker.on('error', reject); }); }}DB 커넥션 풀 튜닝
섹션 제목: “DB 커넥션 풀 튜닝”HikariCP (Spring Boot / JVM)
섹션 제목: “HikariCP (Spring Boot / JVM)”spring: datasource: hikari: maximum-pool-size: 20 # 최대 커넥션 수 minimum-idle: 5 # 최소 유휴 커넥션 connection-timeout: 3000 # 커넥션 획득 대기 최대 3초 idle-timeout: 600000 # 유휴 커넥션 10분 후 반납 max-lifetime: 1800000 # 커넥션 최대 수명 30분 validation-timeout: 1000 # 커넥션 유효성 검사 1초HikariCP 적정 풀 크기 공식 (PostgreSQL 권장):
풀 크기 = (CPU 코어 수 × 2) + 유효 스핀들 수
예시: 8코어, SSD 환경→ (8 × 2) + 1 = 17 → 20개로 반올림
※ 이 공식은 놀랍도록 작은 값을 제시합니다. DB 서버 관점에서 커넥션이 많을수록 경합이 증가합니다.SQLAlchemy (Python)
섹션 제목: “SQLAlchemy (Python)”from sqlalchemy import create_engine
engine = create_engine( "postgresql://user:pass@host/db", pool_size=10, # 기본 풀 크기 max_overflow=20, # pool_size 초과 시 추가 생성 (임시) pool_timeout=3, # 커넥션 대기 최대 3초 pool_recycle=1800, # 30분마다 커넥션 갱신 (방화벽 끊김 방지) pool_pre_ping=True, # 사용 전 커넥션 유효성 확인)Redis 커넥션 풀 (ioredis)
섹션 제목: “Redis 커넥션 풀 (ioredis)”const Redis = require('ioredis');
const redis = new Redis({ host: 'redis-host', port: 6379, maxRetriesPerRequest: 3, // 커넥션 풀 설정 lazyConnect: true, enableOfflineQueue: false, // 연결 끊기면 즉시 실패 (큐 쌓임 방지)});
// 클러스터 환경const cluster = new Redis.Cluster(['redis-node:6379'], { scaleReads: 'slave', // 읽기는 슬레이브로 분산 maxRedirections: 3,});풀 고갈 탐지
섹션 제목: “풀 고갈 탐지”커넥션 풀 고갈 증상:- 에러: "Connection pool timeout" / "HikariPool-1 - Connection is not available"- 응답 시간: 요청이 들어올수록 3초, 5초, 30초로 증가- APM: DB Span 앞에 긴 대기 시간(Connection wait)Prometheus로 HikariCP 모니터링
섹션 제목: “Prometheus로 HikariCP 모니터링”# Prometheus 스크레이프 대상 추가# Spring Boot Actuator + Micrometer 연동 시 자동 노출# 활성 커넥션 수hikaricp_connections_active{pool="HikariPool-1"}
# 대기 중인 요청 수 (이 값이 올라가면 위험)hikaricp_connections_pending{pool="HikariPool-1"}
# 커넥션 획득 평균 대기 시간 (ms)hikaricp_connections_acquire_seconds_sum / hikaricp_connections_acquire_seconds_count * 1000다음 단계
섹션 제목: “다음 단계”이 가이드를 내 서비스에 직접 적용해 보세요.
TestForge 무료 스캔 시작 →