웹 컨테이너(WAS)가 실행되면서 DB와 미리 Connection을 해놓은 객체들을 Pool에 저장해 두었다가, 클라이언트 요청이 오면 Connection을 빌려주고 처리가 끝나면 다시 Connection을 Pool에 반납하는 방식
먼저, Connection Pool을 사용하지 않는 절차를 알아보자.
클라이언트에서 DB 접근이 필요한 요청을 보내게 되면, 매번 새로운 커넥션을 생성하고 인증하는 절차를 반복해야 한다. 이러한 작업이 자주 발생되고 그 양이 많아지면, 네트워크에 부하가 걸리게 되고 결과적으로 성능 저하로 이어지게 된다.
이를 해결하는 것이 Connection Pool이다.
Connection Pool을 쉽게 설명하면, 매번 실행할 Connection을 미리 만들어 두는 것이다. 그러다가 DB 접근이 필요한 요청이 들어오면 DB까지 가서 Connection을 생성하지 않고, 이미 만들어 둔 Connection을 사용하는 것이다. 이를 담아 두는 것을 Connection Pool이라고 한다.
그럼 Connection Pool을 적용한 절차는 어떨까?
이처럼 요청마다 DB 드라이버까지 접근하지 않아도 커넥션 풀에서 가져갔다가 다 쓰면 반납하는 일종의 대여식으로 동작한다.
애플리케이션 시작
Connection Pool에 Connection이 채워지는 시기는 기본적으로 애플리케이션이 시작할 때 초기화 된다. 이 과정에서 설정된 최대 Connection 수에 따라 미리 채워지게 된다.
MySQL 기준 기본적으로 10개의 Connection 수를 Pool에 담다 둔다.
그리고 사용한 Connection이 모든 작업을 마치고 반납할 때는 통신을 끊는 것이 아닌 기존 Pool에 담아 반납한다.
아래는 Connection Pool을 간단하게 구현한 코드다.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class ConnectionPool {
private List<Connection> connectionPool;
private final int MAX_POOL_SIZE = 10;
public ConnectionPool() throws SQLException {
connectionPool = new ArrayList<>();
initializePool();
}
private void initializePool() throws SQLException {
for (int i = 0; i < MAX_POOL_SIZE; i++) {
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "user", "password");
connectionPool.add(connection); // 생성한 Connection을 Pool에 추가
}
}
public Connection getConnection() {
if (connectionPool.isEmpty()) {
// 커넥션이 없는 경우, 새로운 커넥션 생성 로직 추가 가능
}
return connectionPool.remove(0); // Pool에서 커넥션을 가져옴
}
public void returnConnection(Connection connection) {
connectionPool.add(connection); // 사용 후 커넥션을 Pool에 반환
}
}
만일, 이미 운영 중 이거나 개발 중인 프로젝트에서 뒤늦게 Connection Pool을 적용하고 싶어졌다고 해보자.
그럼 코드를 다 갈아버리고 새로 짜야할까?
이를 효율적으로 해결하는 방법이 있는데, 바로 DataSource 인터페이스를 이용하여 Connection 획득하는 방법을 추상화하는 것이다. getConnection()이라는 메서드를 DriverManager와 Connection Pool이 구현해두면, 나중에 Connection Pool을 사용하는 상황으로 바뀌어도 언제든지 getConnection() 메서드를 통해 연결할 수 있다.
동시 접속자(Thread)가 많으면 Connection Pool의 Size만큼 Connection을 보내는데, 이를 초과하는 접속자는 순서대로 대기를 하게 된다.
여기서 WAS에서 Connection Pool을 크게 설정하면 메모리 소모가 큰 대신 많은 사용자가 대기시간이 줄어들고, 반대로 Connection Pool을 적게 설정하면 그 만큼 대기시간이 길어진다.
뭐든 과하면 안되는 것처럼 WAS의 Connection Pool의 Size를 크게 잡는다고 좋은 것은 아니다.
이는 WAS의 성능에 영향을 줄 수 있는데, 가장 크게 영향을 주는 Thread와 Connection Pool의 개수에 대해 알아보자.
Thread와 Connection Pool의 개수는 메모리에 직접적으로 관련이 있다. 즉, Thread와 Connection Pool이 많을 수록 메모리를 많이 점유하게 된다.
그럼 효과적으로 값을 설정하는 방법은 무엇일까?
중요한 것은 Thread와 Connection Pool의 개수를 잘 맞추는 것인데, 이는 서비스마다 동시접속자의 수와 Connection Pool을 사용하는 빈도가 서로 다르기에 직접 운영 서버에서 확인하는 것이 베스트라고 생각한다.
이를 확인했다면, Connection Pool은 Thread 수보다 조금 적게 설정한다. 그 이유는 모든 요청이 Connection Pool을 요구하지 않기 때문이다.
물론, 직접 운영 중인 서비스에 적절한 수치를 찾는 것이 가장 좋다.