
대규모 애플리케이션에서 데이터베이스 연결 관리는 성능과 안정성에 크리티컬한 영향을 미칩니다. 이번 포스팅에서는 yoonnsshop의 대량의 상품 데이터에서의 조회 트래픽 급증 상황에서 발생한 데이터베이스 성능 문제를 다룹니다. 특히 HikariCP 라이브러리를 사용한 Database Connection Pool의 최적화가 어떻게 이러한 문제를 해결할 수 있는지 탐구합니다. 이 과정에서 Connection Pool 크기 설정과 MySQL 서버 설정을 조정하며 성능 변화를 측정하고 분석했습니다. 이를 통해 Connection Pool 최적화의 복잡성과 중요성을 이해하고, 실제 운영 환경에서의 효과적인 대처 방법을 모색하고자 합니다.
Database Connection Pool은 데이터베이스 연결을 효율적으로 관리하기 위한 기술입니다. 이는 미리 일정 수의 데이터베이스 연결을 생성하고 유지하며, 애플리케이션의 요청에 따라 이를 재사용합니다.
이를 통해 연결 생성 및 해제에 따른 오버헤드를 줄이고, 리소스 사용을 최적화하며, 애플리케이션의 성능과 확장성을 향상시킵니다.
| * | Contents |
|---|---|
| Title | Databse Connection Pool 최적화 |
| Trigger | 상품 데이터 대량 삽입 후 조회 트래픽 급증 |
| Problem | 데이터베이스 처리 시간 증가로 인한 성능 저하 |
| Action | Database Connection Pool 관련 설정 조정 |
| Benefit | 커넥션 재사용을 통해 커넥션 생성/해제 오버헤드 감소 및 처리량 향상 |
HikariCP는 이 외에도 다양한 설정 옵션을 제공하여 커넥션 풀의 동작을 세밀하게 제어할 수 있습니다. 예를 들어, 최소 유휴 커넥션 수, 유휴 커넥션의 최대 생존 시간, 커넥션 타임아웃 등을 설정할 수 있습니다. 실제로 운영 환경에서는 이들을 종합적으로 고려하여 최적의 성능을 달성할 수 있습니다.
(자세한 설정 관련 내용은 HikariCP의 공식 문서에서 확인하실 수 있습니다.)
⌈code⌋ build.gradle
implementation 'org.springframework.boot:spring-boot-starter-actuator'
⌈code⌋ application.properties
management.endpoints.web.exposure.include=metrics # actuator 엔드포인트 노출 설정
management.metrics.enable.hikaricp=true # HikariCP 메트릭스 활성화 설정. 커넥션 풀의 상태, 사용량 등의 정보를 모니터링할
> curl -s "http://localhost:8080/actuator/metrics/hikaricp.connections.active"
{"name":"hikaricp.connections.active","description":"Active connections","measurements":[{"statistic":"VALUE","value":0.0}],"availableTags":[{"tag":"pool","values":["HikariPool-1"]}]}%
Connection Pool의 상태를 모니터링하기 위해 다음과 같은 스크립트들을 작성했습니다:
⌈code⌋ monitor_network_connections.sh
#!/bin/bash
while true
do
count=$(lsof -i | wc -l)
echo "$(date): $count active connections"
sleep 1 # 1초마다 실행
done
⌈code⌋ hikari_monitoring.sh
#!/bin/bash
while true
do
# HikariCP 커넥션 풀 상태 정보 가져오기
active_connections=$(curl -s "http://localhost:8080/actuator/metrics/hikaricp.connections.active" | jq '.measurements[0].value')
idle_connections=$(curl -s "http://localhost:8080/actuator/metrics/hikaricp.connections.idle" | jq '.measurements[0].value')
total_connections=$(curl -s "http://localhost:8080/actuator/metrics/hikaricp.connections" | jq '.measurements[0].value')
# 현재 시간과 커넥션 풀 상태 출력
echo "$(date): Active Connections: $active_connections, Idle Connections: $idle_connections, Total Connections: $total_connections"
sleep 1 # 1초마다 실행
done
⌈code⌋ application.properties
spring.datasource.hikari.maximum-pool-size=10
아래와 같은 설정 값을 통해 maximum-pool-size를 조정할 수 있습니다. 조정을 통한 테스트 결과입니다.
| maximum-pool-size | Requests/sec (RPS) | Transfer/sec (TPS) |
|---|---|---|
| 10 | 2293.61 | 5.33MB |
| 20 | 2899.10 | 6.73MB |
| 1000 | 3975.10 | 9.23MB |
그러나 실제로는 maximum-pool-size=1000 케이스의 경우, MySQL의 max_connections 설정(151)에 의해 제한되고 있는 것을 확인했습니다.
MySQL에 설정된 max_connections 갯수 제한으로 151로 설정되어있었습니다.
SHOW VARIABLES LIKE 'max_connections';
+-----------------+-------+
| Variable_name | Value |
| max_connections | 151 |
+-----------------+-------+
이에 따라, max_connections 값을 1000으로 셋팅해주었습니다.
SET GLOBAL max_connections = 1000;
| MySQL:max_connections | maximum-pool-size | Requests/sec (RPS) | Transfer/sec (TPS) |
|---|---|---|---|
| 151 | 1000 | 3975.10 | 9.23MB |
| 1000 | 1000 | 1809.09 | 4.19MB |
Active Connection 수 제한
Java 애플리케이션의 스레드 수가 실제 Active Connection 수를 제한하는 것으로 추측됩니다. 이는 Database Connection Pool 크기를 증가시켜도 실제 사용되는 연결 수는 애플리케이션의 동시 처리 능력에 의해 제한될 수 있음을 의미합니다.
성능 저하 원인에 대한 추측
MySQL의 max_connections 설정을 변경할 경우, 연결된 애플리케이션의 재시작이 필요할 수 있습니다. 실제로 Mysql의 max_connections 설정을 1000에서 151로 변경해도 모니터링하는 Active Connection의 수와 사용중인 네트워크 연결 수가 유지되는 것으로 나타났습니다. 이 때, 애플리케이션을 재시작하여 자원을 정상적으로 해제하고 새로운 설정을 적용해주어야 합니다.
이 실험을 통해 Database Connection Pool 최적화의 중요성과 복잡성을 확인했습니다. 주요 발견사항은 다음과 같습니다:
결론적으로, Database Connection Pool 최적화는 단순한 설정 조정 이상의 복잡한 과정입니다. 데이터베이스 서버 설정, 애플리케이션 특성, 실제 부하 상황을 종합적으로 고려한 세심한 조정이 필요합니다. 지속적인 모니터링, 튜닝, 그리고 실제 운영 환경에서의 테스트를 통해 최적의 설정을 찾아나가는 것이 중요합니다.