DB Connection 이란 애플리케이션과 데이터베이스 서버가 통신할 수 있도록 하는 기능이다.
아래 코드는 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 객체가 반환된다.
자바가 다양한 데이터베이스를 지원할 수 있는 이유는 각 데이터베이스에 맞는 JDBC 드라이버가 구현되어 있기 때문이다.
이 드라이버는 Java 애플리케이션과 각 DB 간의 통신을 중개하여, 동일한 JDBC API 를 사용하여도 각 DB에 적합한 프로토콜로 요청을 전달하고 응답을 받을 수 있다.
// 드라이버는 DB 공급업체(Oracle, MySQL ..) 에서 제공하는 JDBC 드라이버 jar 파일을 classpath 에 추가하여 로드한다.
connection.close();
그런데 DB 를 연결할 때마다 Connection 객체를 새로 만드는 것은 비용이 많이 들고 비효율적이다.
그럼 어떻게 해야할까 ?
위에서 살펴본 Connection 객체의 반복적인 생성과 해제를 피하고, 효율적으로 데이터베이스 연결을 관리하기 위해 Connection 풀링이 사용된다.
DB Connection Pool 이란, 데이터베이스로의 추가 요청이 필요할 때 연결을 재사용할 수 있도록 관리되는 데이터베이스 연결의 캐시이다.
간단하게 말하자면, 애플리케이션 시작 시 미리 일정 수의 Connection 객체를 생성하여 Pool 에 보관한다. 이후에 DB 작업이 필요할 때마다 Pool 에서 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();
}
}
}
}
따라서 커넥션 풀의 크기를 적절히 조절하여 최적의 성능을 유지해야 한다.
(참고) Hikari CP는 적절한 Connection Pool의 크기를 1 connections = ((core_count) * 2) + effective_spindle_count) 로 정의하고 있다.