커넥션 풀과 데이터 소스에 대해 알아보자.
JDBC 포스팅을 보면 DB 드라이버를 통해 항상 커넥션을 획득하게 된다.
그런데 커넥션을 획득하는 과정이 단순하지 않다.
1. Connection을 확대해보면 다음과 같다.
이렇게 DB 드라이버가 커넥션 생성이 완료되었다는 응답을 받으면 커넥션 객체를 생성해서 반환한다.
이처럼 커넥션을 생성하는 과정은 단순하지 않고 시간이 많이 소모된다.
그래서 매번 커넥션을 획득하다보면 응답 속도에 영향을 줄 수 있어 사용자에게 좋지 않은 서비스를 제공할 수 있다.
그래서 이러한 문제를 해결하기 위해 미리 커넥션을 생성해두고 사용하는 커넥션 풀이라는 개념이 등장했다.
이렇게 미리 커넥션을 생성해두기떄문에 드라이버를 통해서 커넥션을 획득할 필요가 없고 이제 Application Logic은 커넥션 풀에서 바로 커넥션을 꺼내서 사용할 수 있다.
그리고 이 커넥션들은 모두 DB와 연결되어 있는 커넥션이기때문에 바로 쿼리문을 DB로 전송할 수 있다.
그리고 커넥션을 사용 후 다시 커넥션 풀에 반환해주면 되기 떄문에 재활용할 수 있다.
커넥션 풀은 오픈소스 커넥션 풀을 자주 사용하며 주로 hikariCP를 주로 사용함.
public interface DataSource extends CommonDataSource, Wrapper {
Connection getConnection() throws SQLException;
Connection getConnection(String username, String password)
throws SQLException;
...
코드로 더 자세히 알아보자
H2 DB 연결을 위한 기본 정보
public abstract class ConnectionConst {
public static final String URL = "jdbc:h2:tcp://localhost/~/test";
public static final String USERNAME = "sa";
public static final String PASSWORD = "";
}
@Test
void driverManager() throws SQLException {
Connection con1 = DriverManager.getConnection(URL, USERNAME, PASSWORD); //커넥션 획득
Connection con2 = DriverManager.getConnection(URL, USERNAME, PASSWORD);
log.info("connection={}, class={}", con1, con1.getClass());
log.info("connection={}, class={}", con2, con2.getClass());
}
@Test
void dataSourceDriverManager() throws SQLException{
DriverManagerDataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
Connection con1 = dataSource.getConnection();
Connection con2 = dataSource.getConnection();
log.info("connection={}, class={}", con1, con1.getClass());
log.info("connection={}, class={}", con2, con2.getClass());
}
@Test
void dataSourceConnectionPool() throws SQLException, InterruptedException {
//커넥션 풀
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(URL);
dataSource.setUsername(USERNAME);
dataSource.setPassword(PASSWORD);
dataSource.setMaximumPoolSize(10);
dataSource.setPoolName("myPool");
Connection con1 = dataSource.getConnection();
Connection con2 = dataSource.getConnection();
log.info("connection={}, class={}", con1, con1.getClass());
log.info("connection={}, class={}", con2, con2.getClass());
Thread.sleep(1000);
}
로그를 확인해보자.
너무 길어서 약간 수정했다.
[myPool connection adder] Added connection conn2: url=jdbc:h2:tcp://localhost/~/test user=SA
[myPool connection adder] Added connection conn3: url=jdbc:h2:tcp://localhost/~/test user=SA
[myPool connection adder] Added connection conn4: url=jdbc:h2:tcp://localhost/~/test user=SA
[myPool connection adder] Added connection conn5: url=jdbc:h2:tcp://localhost/~/test user=SA
[myPool connection adder] Added connection conn6: url=jdbc:h2:tcp://localhost/~/test user=SA
[myPool connection adder] Added connection conn7: url=jdbc:h2:tcp://localhost/~/test user=SA
[myPool connection adder] Added connection conn8: url=jdbc:h2:tcp://localhost/~/test user=SA
[myPool connection adder] Added connection conn9: url=jdbc:h2:tcp://localhost/~/test user=SA
참고로 만약 커넥션 풀에 커넥션이 아직 생성되지 않았을 때, 커넥션을 획득할려고 하면 약간 대기하면서 커넥션을 획득한다.
그리고 만약 커넥션 풀이 가득찼을 때, 모든 커넥션을 다 사용하고있는데 또 커넥션 획득을 요구하는 경우에는 언제까지 대기할 것인지, 얼마정도 기다렸을 때 예외를 터뜨릴 것인지 등을 설정하는게 중요하다.
본 포스팅은 스프링 DB 1편를 공부하면서 정리한 글입니다.