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

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 설정으로 커넥션 누수 감지