프로그래밍

[DB] 세션 수가 계속 증가되는 현상, 커넥션 누수 관련 점검(Connection Pool, JDBC)

가시가되어 2025. 4. 2. 11:22

1. 커넥션 누수 현상 확인 방법

1.1 DB 세션 모니터링

DB에서 현재 활성화된 세션을 확인하여, 어플리케이션에서 생성한 세션이 지속적으로 유지되고 있는지 점검

MySQL

SHOW PROCESSLIST;

SELECT * FROM information_schema.PROCESSLIST WHERE COMMAND != 'Sleep';
  • Sleep 상태의 세션이 많다면 커넥션이 닫히지 않고 유지되고 있는 것일 수 있음.

PostgreSQL

SELECT pid, usename, application_name, client_addr, state FROM pg_stat_activity;
  • state가 idle in transaction 상태로 오래 유지되면 커넥션이 닫히지 않은 것.

Oracle

SELECT SID, STATUS, SCHEMANAME, MACHINE, PROGRAM FROM V$SESSION WHERE STATUS = 'ACTIVE';
  • 특정 프로그램에서 지속적으로 세션이 유지되는지 확인.

1.2 JDBC Connection 누수 감지 (Active Connection 수 확인)

1) Connection Pool 사용 시 현재 활성 커넥션 수 체크

만약 HikariCP 같은 커넥션 풀을 사용 중이라면, 다음 코드로 현재 커넥션 수를 확인

HikariDataSource hikariDataSource = (HikariDataSource) dataSource; System.out.println("Active connections: " + hikariDataSource.getHikariPoolMXBean().getActiveConnections());
  • getActiveConnections() 값이 지속적으로 증가하면 커넥션이 누수되고 있을 가능성이 큼.

2) 직접 Connection 수를 출력하는 코드 추가 (로깅)

try (Connection connection = dataSource.getConnection()) {
    System.out.println("DB Connection opened: " + connection);
} catch (SQLException e) {
    e.printStackTrace();
}
  • 커넥션이 닫히지 않으면 로그를 분석해서 누수를 확인할 수 있음.

2. 누수 원인 및 해결 방법

2.1 Connection Close 누락 여부 점검

JDBC 사용 시 close() 메서드를 호출하지 않으면 커넥션이 유지되므로, 아래 방식처럼 try-with-resources를 사용하여 자동으로 닫히도록 처리할 수 있음.

문제 코드 (Connection 닫지 않음)

Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
    System.out.println(rs.getString("name"));
}
// conn.close();  ← 이게 빠지면 누수 발생
 

올바른 코드 (try-with-resources 사용)

try (Connection conn = dataSource.getConnection();
     PreparedStatement ps = conn.prepareStatement("SELECT * FROM users");
     ResultSet rs = ps.executeQuery()) {
    
    while (rs.next()) {
        System.out.println(rs.getString("name"));
    }

} catch (SQLException e) {
    e.printStackTrace();
}
  • try-with-resources를 사용하면 Connection, PreparedStatement, ResultSet이 자동으로 닫힘.

2.2 Connection Pool 설정 확인

커넥션 풀을 사용할 때, 설정이 잘못되면 커넥션이 닫히지 않고 계속 유지

HikariCP 설정 예시

# HikariCP 설정 (application.properties)
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=2
spring.datasource.hikari.idle-timeout=30000  # 30초 후 유휴 커넥션 종료
spring.datasource.hikari.max-lifetime=1800000  # 30분 후 커넥션 재생성
  • idle-timeout을 너무 크게 설정하면 사용되지 않는 커넥션이 유지될 수 있음.

C3P0 설정 예시

spring.datasource.c3p0.max-size=10
spring.datasource.c3p0.max-idle-time=30  # 30초 후 유휴 커넥션 종료

 


2.3 오랜 트랜잭션 방지

commit() 또는 rollback()을 하지 않으면 트랜잭션이 유지되면서 커넥션이 반환되지 않는 경우가 있음.

문제 코드 (트랜잭션 종료 누락)

Connection conn = dataSource.getConnection();
conn.setAutoCommit(false);
PreparedStatement ps = conn.prepareStatement("UPDATE users SET name = ? WHERE id = ?");
ps.setString(1, "NewName");
ps.setInt(2, 1);
ps.executeUpdate();
// conn.commit();  ← 빠지면 커넥션이 계속 유지됨
conn.close();

올바른 코드

try (Connection conn = dataSource.getConnection()) {
    conn.setAutoCommit(false);
    
    try (PreparedStatement ps = conn.prepareStatement("UPDATE users SET name = ? WHERE id = ?")) {
        ps.setString(1, "NewName");
        ps.setInt(2, 1);
        ps.executeUpdate();
    }
    
    conn.commit();
} catch (SQLException e) {
    e.printStackTrace();
}
  • 트랜잭션을 사용하면 반드시 commit() 또는 rollback()을 호출해야 함.

2.4 Debugging: Connection Leaks 추적

HikariCP LeakDetectionThreshold 설정

HikariCP를 사용한다면, 누수되는 커넥션을 감지할 수 있도록 설정

spring.datasource.hikari.leak-detection-threshold=2000  # 2초 이상 사용된 커넥션 로그 출력
  • 2초 이상 반환되지 않는 커넥션이 있으면 로그로 감지 가능.

Apache Commons DBCP Debug 설정

DBCP를 사용 중이라면, logAbandoned=true 옵션을 활성화해서 누수된 커넥션을 감지할 수 있어.

spring.datasource.dbcp2.log-abandoned=true
spring.datasource.dbcp2.remove-abandoned-on-maintenance=true
spring.datasource.dbcp2.remove-abandoned-on-borrow=true
spring.datasource.dbcp2.remove-abandoned-timeout=60

3. 결론

DB에서 활성 세션 확인 (SHOW PROCESSLIST;, pg_stat_activity, V$SESSION)
JDBC에서 Connection, Statement, ResultSet을 닫고 있는지 확인 (try-with-resources 사용)
커넥션 풀 설정 확인 (idle-timeout, max-lifetime)
트랜잭션이 commit/rollback 없이 유지되고 있는지 점검
LeakDetectionThreshold 또는 logAbandoned 설정으로 커넥션 누수 감지