CS/DB

DB Connection Pool

GongCho 2025. 2. 27. 11:59

스프링부트 애플리케이션 성능을 최적화하기 위한 방법 중 DB Connection Pool을 늘리는 방법이 있다.

이번 글은 DB Connection Pool이 무엇인지 알아보고자 한다.

 

DB Connection Pool이란 애플리케이션과 DB서버가 통신할 수 있도록 하는 기능이다.

아래 코드는 Java의 DB Connection을 JDBC를 이용해 구현한 예이다.

public Connection getConnection() {

        try {
            return DriverManager.getConnection("jdbc:mysql://" + SERVER + "/" + DATABASE + OPTION, USERNAME, PASSWORD);
        } catch (final SQLException e) {
            System.err.println("DB 연결 오류:" + e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

연결에 성공하면 Connection 객체가 반환된다.

Connection 객체가 반환되는 과정

  1. DB 드라이버 로드 : 우선 사용하려는 DB에 해당하는 JDBC드라이버를 로드해야한다.
  2. DB 연결 정보 설정 : 연결하려는 DB의 URL, 사용자 이름, 비밀번호 등의 연결정보를 설정한다.
  3. Connection 객체 생성 : DriverManager클래스를 사용하여 DB와 연결하고 이 연결을 나타내는 Connection객체를 얻는다.

Connection 이후

  1. Connection 객체 사용 : 반환된 Connection 객체를 사용하여 DB관련 작업을 수행한다.
  2. 연결 종료 : DB작업을 마치면, Connection 객체를 명시적으로 닫아야한다. 리소스 누수를 방지하고 DB연결을 제대로 해제하기 위함이다. connection.close();

DB를 연결할때마다 Connection 객체를 새로 만드는 것이 좋지 않은 이유

  1. 네트워크 비용
    • DB연결을 설정하는 과정은 네트워크 통신을 동반한다.
    • 새로운 Connection객체를 생성할때마다 DB서버와 네트워크 연결을 해야하며, 이 과정은 일정 시간이 소요된다.
    • 따라서, 빈번한 Connection생성은 불필요한 네트워크 비용을 발생시킨다.
  2. 리소스 사용
    • 각 Connection 객체는 DB서버의 연결세션을 나타낸다.
    • DB서버는 동시에 처리할 수 있는 연결 세션 수에 제한이 있으며, 무분별한 Connection생성은 서버 리소스를 소모할 수 있다.
  3. 비용적 측면
    • Connection객체 생성은 시간과 메모리를 소모한다.
    • 특히 DB연결 설정 및 인증과정은 비용이 큰 작업이다.

DB Connection Pool

위에서 살펴본 Connection 객체의 반복적인 생성과 해제를 피하고 효율적으로 DB연결을 관리하기 위해 Connection Pool이 사용된다.

DB Connection Pool이란, DB의 추가 요청이 필요할 때, 연결을 재사용할 수 있도록 관리되는 DB연결의 캐시이다.

간단하게 말하자면, 애플리케이션 시작 시, 미리 일정 수의 Connection객체를 생성하여 Pool에 보관한다.

이후에 DB작업이 필요할때마다 Pool에서 Connection객체를 가져다 사용하고 작업이 끝나면 Pool에 반환한다.

Connection Pool 장점

  1. 성능 향상
    • 미리 연결된 DB연결을 Pool에 유지하고 요청이 들어올때마다 해당 연결을 재사용함으로써 응답시간을 단축하고 애플리케이션의 성능을 향상시킨다.
  2. 자원 관리
    • 연결을 생성하고 유지하는데 필요한 자원을 최적화한다. 불필요한 연결을 만들지 않고 연결을 재사용함으로써 메모리와 CPU등의 자원을 효율적으로 관리할 수 있다.
  3. 동시성 관리
    • 동시에 여러 요청을 처리할 수 있는 연결을 제공하므로, 다수의 사용자가 동시에 애플리케이션에 접속해도 안정적으로 처리할 수 있다.
  4. 연결 풀링
    • 연결의 개수를 제한하고, 초과하는 요청이 들어올 경우 대기하도록 처리함으로써, DB서버의 부하를 관리하고 과부하를 방지한다.
  5. Connection 오버헤드 감소
    • 반복적인 DB연결/해제 작업에 따른 오버헤드를 감소시킨다.

Connection Pool 단점

  1. 리소스 사용
    • 일정 수의 연결을 미리 생성 및 유지해야하므로, 메모리 등의 리소스를 일정 부분 소비한다.
  2. 설정 및 관리의 복잡성
    • 다양한 환경에서 최적의 성능을 발휘하기 위해서는 일정한 관리와 모니터링이 필요하다.
    • 설정 파라미터의 조절이 필요한 경우에는 초기 설정 및 튜닝에 시간이 소요될 수 있다.
  3. 커넥션 누수
    • 애플리케이션에서 연결을 올바르게 반환하지 않거나 예외가 발생하는 경우, 커넥션 풀에서 연결이 제대로 반환되지 않을 수 있다. 이 경우, 커넥션 누수가 발생할 수 있다.

Connection Pool 사용 예시

Connection Pool의 종류 중 하나인 HikariCP를 사용한 예시코드이다.

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class ConnectionPoolExample {

    public static void main(String[] args) {
        // HikariCP 설정
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/mydatabase");
        config.setUsername("username");
        config.setPassword("password");

        // HikariCP 데이터소스 생성
        HikariDataSource dataSource = new HikariDataSource(config);

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            // 커넥션 풀에서 커넥션 획득
            connection = dataSource.getConnection();

            // SQL 쿼리 실행
            String sql = "SELECT * FROM users WHERE id = ?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1, 1); // 예시로 사용자 ID가 1인 사용자 조회
            resultSet = preparedStatement.executeQuery();

            // 결과 처리
            if (resultSet.next()) {
                String username = resultSet.getString("username");
                System.out.println("Username: " + username);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 리소스 해제 (역순으로 해제)
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close(); // 커넥션을 풀에 반환
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            // HikariCP 데이터소스 종료
            if (dataSource != null) {
                dataSource.close();
            }
        }
    }
}

Connection Poo이 커지면 성능이 무조건 좋아질까?

Connection Pool을 크게 설정하면

  • 많은 메모리를 사용하지만, 동시에 많은 사용자가 대기 시간이 줄어들어 성능이 향상될 수 있다.

Connection Pool을 작게 설정하면

  • 메모리 소모는 줄어들지만, 동시 접속자가 많아지면 대기시간이 길어질 수 있다.

Hikari CP는 적절한 Connection Pool의 크기를 1 connections = ((core_count) * 2) + effective_spindle_count) 로 정의하고 있다.