개발/JAVA

could not open JDBC Connection

잡부여우 2025. 8. 3. 11:43

JDBC 커넥션 오류. 또 너냐.

could not open JDBC Connection. 지겹게 보는 에러다.
원인은 다양하지만, 해결책은 보통 정해져 있다. 순서대로 확인하자.

 

1. 커넥션 풀 설정 조정

가장 먼저 볼 것. WAS의 커넥션 풀이 부족하면 당연히 에러가 난다. 부하에 비해 풀이 작지 않은지 확인한다.

application.yml (HikariCP 기준)

spring:
  datasource:
    hikari:
      maximum-pool-size: 20 # 여기. 부하에 맞게 늘리자.

데이터베이스의 max_connections도 고려해야 한다. 무작정 늘리는 건 답이 아니다.

 

2. DB 커넥션이 잘 닫히는지 확인

기본 중의 기본. 커넥션 누수는 풀을 말려버린다. try-with-resources를 쓰면 실수는 줄어든다.

// 괄호 안에서 생성된 자원은 알아서 닫힌다.
try (Connection conn = dataSource.getConnection();
     PreparedStatement pstmt = conn.prepareStatement(sql);
     ResultSet rs = pstmt.executeQuery()) {
    // ... 로직 ...
} catch (SQLException e) {
    // ... 예외 처리 ...
}

코드를 의심하자. 항상.

 

3. Slow Query나 Long Transaction 확인

느린 쿼리나 긴 트랜잭션은 커넥션을 오래 붙잡는다. 결국 다른 요청이 커넥션을 못 가져간다.

APM(Application Performance Management) 툴이 있으면 편하다.
없으면 DB 로그라도 뒤져봐야 한다.

 

4. 실제 max_connections 확인

DB가 최대로 허용하는 커넥션 수를 확인한다.

-- MySQL/MariaDB 기준
SHOW VARIABLES LIKE 'max_connections';

이 값보다 HikariCP의 maximum-pool-size가 크면 의미 없다.

4-1. 현재 커넥션 수와 비교

현재 DB에 연결된 커넥션 수도 확인해 보자.

-- MySQL/MariaDB 기준
SHOW STATUS LIKE 'Threads_connected';

max_connections에 근접하고 있다면 위험 신호다. 원인을 찾아야 한다.

5. spring-retry 적용

일시적인 네트워크 문제나 DB 장애로 커넥션을 못 가져올 수도 있다. 이럴 때 spring-retry는 유용하다. 몇 번 더 시도하고, 그래도 안 되면 실패 처리한다.


의존성 추가 (Maven)

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
</dependency>


@EnableRetry 추가

@Configuration
@EnableRetry
public class AppConfig {
    // ...
}


서비스 메소드에 적용

import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import java.sql.SQLException;

@Service
public class MyService {

    @Retryable(
        value = {SQLException.class}, // SQLException 발생 시 재시도
        maxAttempts = 3,              // 최대 3번
        backoff = @Backoff(delay = 2000) // 2초 간격으로
    )
    public void someDatabaseOperation() {
        // ... DB 작업 ...
    }
}


@Retryable은 만능이 아니다.
위의 모든 것이 안된다면 신중하게 사용해야 한다.

여기까지 확인하면 대부분 해결된다.

이제 퇴근하자.