JMeter) HikariCP pool size 조정을 통한 성능 개선 및 검증

Wonjun Lee·2025년 8월 21일
post-thumbnail

Connection Pool & Tomcat Thread 튜닝 테스트 기록

서론

최근 웹 서비스에서 성능 저하와 불안정한 응답 시간이 발생하여 원인을 분석하던 중, DB Connection Pool(HikariCP)과 Tomcat Thread 수의 설정이 주요한 요인으로 보였다. 이에 따라 다양한 설정 값을 적용해가며 성능을 측정하고, 최적의 조합을 찾기 위한 테스트를 수행하였다.
테스트 결과는 단순히 풀 사이즈를 크게 늘리는 것이 성능 개선으로 이어지지 않으며, 적절한 비율을 유지하는 것이 중요하다는 점을 보여준다.

테스트 목적

Spring Boot 애플리케이션의 DB Connection Pool(HikariCP) 및 Tomcat Thread 설정 최적화

Throughput(처리량), 응답 속도, 안정성을 고려한 적정 조합 도출

테스트 환경

대상 API: 강좌 카드 조회 API

요청 스레드 수: 1000

요청 횟수: 각 1000회

요청 방식: 인증 없이 API 호출(허용됨.)

초기 설정 (Baseline)

Tomcat threads: 10 (default)

HikariCP pool size: 10 (default)

테스트 과정

1. 커넥션 풀 대폭 확장

server.tomcat.threads.max=200
spring.datasource.hikari.maximum-pool-size=159
spring.datasource.hikari.minimum-idle=159
spring.datasource.hikari.connection-timeout=500
spring.datasource.hikari.validation-timeout=1000
spring.datasource.hikari.max-lifetime=1800000
  • 평균 응답시간: 1488ms

  • Throughput: 670 req/s

  • P95, P99 지연시간이 크게 악화

  • Too many connections 에러 발생

해석

풀 사이즈를 과도하게 늘리면 DB 경합과 컨텍스트 스위칭으로 꼬리 지연이 심해진다.

피드백

DB 커넥션이 많긴 하지만 생성된 요청 스레드 수가 더 많아서 기다리는 상황이 발생한다. 또, MySQL의 설정을 확인할 필요성을 느꼈다.

변수명
max_connections151
Threads_connected146
Max_used_connections152
wait_timeout28800
interactive_timeout28800

2. MySQL max_connections 조정

조치사항

SET GLOBAL max_connections = 220; 적용 → 여전히 Too many connections 발생

피드백

SET GLOBAL max_connections = 220;

위 명령어 실행으로 커넥션 수를 조정하고 실행했으나, 에러가 발생했고 확인해보니 값이 원상 복구 되어있었음.

Docker 환경에서 영구 반영 필요했고, CI/CD 파이프라인에 해당 커맨드를 수행하도록 명령어를 수정하였음.

현재 백엔드 아키텍처 상에서 Websocket server도 존재하며, 이 서버도 역시 MySQL에 접근하기 때문에 모든 Connection을 할당하는 것은 바람직하지 않다고 여겨짐.

Default값이 10 이므로, 웹소켓 서버에 10을 할당하고, API 서버에 141을 할당함.

해당 지표로 간단히 테스트해 본 결과

  1. 원시 지표 요약 ( By GPT)
LabelSamplesAverage (ms)Min (ms)Max (ms)Std. Dev (ms)Error %Throughput (req/s)Received KB/sSent KB/sAvg. Bytes
HTTP Request753,6981,48864,900704.710.00133670.451,016.48123.581,552.52
  1. 해석 요약 ( By GPT)
항목평가코멘트
처리량(Throughput)준수약 670 req/s 처리
평균 응답시간보통~1.49s (서비스 특성에 따라 체감)
분산(Std. Dev)높음~705ms → 응답 편차 큼
Tail Latency주의Max ~4.9s → 일부 사용자 지연
안정성(Error%)매우 우수0.0013% 수준의 극저 에러율

피드백

풀사이즈를 크게 잡을수록 DB 경합과 컨텍스트 스위칭 증가로 인해 P95+가 증가할 수 있다.

권장 튜닝 원칙 = 스레드 : 커넥션 = 2 ~ 3 : 1

풀 사이즈는 필요 동시 DB 작업 수 만큼만 필요함.

  • minIdle은 maxPool의 30 ~ 50% 수준.
  • connection-timeout은 2 ~ 5s (500ms 는 스파이크에서 불필요한 실패 유발함.)
  • maxLifetime < DB wait_timeout

처리량을 중시한다면, Pool size를 높게 잡음(80정도)

Tail latency 최적화가 목적이라면, Pool size를 줄임. ⇒ 경합을 완화하여 개선함.

3. 권장 튜닝 원칙 적용

  • 원칙: 스레드 : 커넥션 = 2~3 : 1
server.tomcat.threads.max=280
spring.datasource.hikari.maximum-pool-size=60
spring.datasource.hikari.minimum-idle=24
spring.datasource.hikari.connection-timeout=3000

HikariPool-1 - Connection is not available, request timed out after 3002ms
(total=24, active=24, idle=0, waiting=224)
Too many connections

여전히 Too many connections 에러 발생함.

4. Thread & Pool 조합 실험

4.1. 스레드 = 240, pool size = 24-60

Too many connections 에러 발생함.

4.2. 스레드 = 200, pool size = 60-141

추가 조치사항 : 쿼리 성능 향상을 위해 트랜잭션 적용 -> 커넥션 사용 기간을 조금이나마 단축하기 위함.

LabelSamplesAvgMedian90% Line95% Line99% LineMinMaximumError %ThroughputReceived KB/secSent KB/sec
HTTP Request12170617591393334735434236292020.00822566.61863.43103.72

초반 지표가 너무 나빠서 조기종료.

피드백

일부 조합에서 성능 개선이 있었지만, 전체적으로 큰 차이는 없었음.
그러나, tomcat에서 생성되는 요청 처리용 스레드들이 커넥션을 할당받기를 대기하다가 timeout이 발생하여 실패하는 것을 통해 thread 개수와 connection 수를 맞춰줘야할 필요성을 느낌.

5. Thread와 Pool을 1:1 매칭

server.tomcat.threads.max=200
spring.datasource.hikari.maximum-pool-size=200
spring.datasource.hikari.minimum-idle=50

결과: 여전히 Too many connections 발생

6. 최종 안정화 설정

server.tomcat.threads.max=64
spring.datasource.hikari.maximum-pool-size=64
spring.datasource.hikari.minimum-idle=32
spring.datasource.hikari.connection-timeout=3000

성능 지표 비교

LabelSamplesAvgMedian90% Line95% Line99% LineMinMaximumError %ThroughputReceived KB/secSent KB/sec
default75267014941445196920602558773830.00132%667.301011.72123.00
커넥션 풀 조정 (max = 159, idle = 159)75369814881207309233903731649000.00133%670.451016.48123.58
개선율 (커넥션 풀 조정 vs default)--0.40%-16.5%+57.0% (악화)+64.6% (악화)+45.8% (악화)-14.3%-33.6%거의 동일+0.47%+0.47%+0.47%
t.m=64, p.s=64, s.m=321000000147914151752189021802632700.0%673.521020.15124.31
개선율 (t.m=64, p.s=64, s.m=32 vs default)--1.0%-2.1%-11.0%-8.3%-14.8%+271% (↑)-55.7%개선 (0%)+0.93%+0.84%+1.07%

결론

최종 개선 결과 해석

평균 응답시간(Avg, Median)

default 대비 평균 응답시간 1% 감소, 중앙값은 2.1% 감소.

눈에 띄는 차이는 아니지만, 일관된 응답 속도 개선을 확인할 수 있었다.

꼬리 지연 (P90, P95, P99)

90% Line: 11% 개선

95% Line: 8.3% 개선

99% Line: 14.8% 개선

즉, 전체적인 체감 성능보다는 고부하 상황에서 극단값 지연(Tail Latency)이 줄어든 점이 의미 있다.

Throughput (처리량)

default: 667 req/s → 최적 설정: 674 req/s

0.93% 개선으로 크지는 않지만, 안정적으로 더 많은 요청을 처리할 수 있게 됨.

Error율

default: 0.00132% → 최적 설정: 0.0%

에러가 완전히 제거됨. 안정성 측면에서 큰 개선.


회고.

단순히 커넥션 수를 늘리면, 더 많은 스레드가 할당받아 사용할 수 있을 것으로 생각했으나 틀렸다. MySQL의 내부 설정도 고려해야 한다는 점을 배울 수 있었다.

특히, 관련 자료 조사 과정에서 오히려 커넥션 수를 크게 줄이는 것이 전체 성능을 대폭 향상시킨 사례를 접했는데, 이전에 학부 수업에서 배웠던 병렬처리에서 context switching 비용을 상기할 수 있어 좋았다.

이번 테스트를 기반으로 시스템의 전반적인 조회 성능을 향상시킬 수 있었다. 특히 꼬리 지연을 감소시킴으로써 일반적인 응답시간 감소를 이끌어낼 수 있었다.

profile
Samsung Electronics. SW Engineering.

0개의 댓글