데이터 베이스 커넥션을 획들 할 때는 복잡한 과정을 거치게 된다.
TCP/IP
커넥션을 연결 (3 way handshake 네트워크 동작 발생)TCP/IP
커넥션 연결이 완료되면 ID,PW, 기타 부가정보를 DB에 전달이런 방식은 복잡하고 시간도 많이 소모 → 커넥션을 매번 새로 만듦
이 과정에서 리소스를 매번 사용해야하고 고객이 애플리케이션을 사용할 때, SQL을 실행하는 시간 뿐 아니라 커넥션을 새로 만드는 시간이 추가되어 응답속도에 영향을 주게 된다.
이를 해결하기 위한 아이디어가 Connection Pool이다.
애플리케이션이 시작하는 시점에 커넥션 풀은 필요한 만큼 커넥션을 미리 확보하여 풀에 보관한다. 얼마나 보관하는 지는 서비스의 특징과 서버 스펙에 따라 다르다. 애플리케이션 서버 스펙과 DB 스펙에 따라 상이하기 때문에 성능 테스트를 통하여 적절한 커넥션 풀의 수를 정한다. 무한정 DB에 커넥션이 생성되는 것을 막아 DB를 보호하는 효과도 있다.
커넥션 풀에 들어있는 커넥션들은 TCP/IP
로 DB와 커넥션이 연결되어 있는 상태로 언제든지 즉시 SQL을 DB에 전달 할 수 있다.
DB 드라이버를 통해 새로운 커넥션을 획득하는 것이 아니라, 이미 생성되어 있는 커넥션을 객체 참조로 가져다 쓰는 것으로 애플리케이션 로직은 커넥션 풀에서 받은 커넥션을 사용, SQL을 DB에 전달, 그 결과를 받아 처리한다.
커넥션을 모두 사용하고 나면 커넥션을 종료하는 것이아니라, 다음에 다시 사용할 수 있도록 해당 커넥션을 그대로 커넥션 풀에 반환하게된다. 종료가 아니라 반환 !
대표적인 커넥션 풀 오픈소스는 commons-dbcp2
,tomcat-jdbc pool
,HicariCP
등이 있다.
앞서 설명한 것처럼 커넥션을 얻는 방법은 JDBC DriverManager
를 직접 사용하거나 커넥션 풀을 사용하는 등 다양한 방법이 존재.
만약 JDBC로 개발한 어플리케이션 처럼 DriverManager
를 통해서 커넥션을 획득하다가, 커넥션 풀을 사용하는 방법으로 변경하려면 어떻게 해야 할까 ?
어플리케이션 코드도 함께 변경해야 할 것이다. 의존 관계가 DriverManager
에서 HikariCP
로 변경되기 때문이다. 그리고 둘의 사용법도 다를 것이다.
자바에서는 이런 문제를 해결하기 위해 javax.sql.DataSource
라는 인터페이스를 제공한다.
즉, 커넥션을 획득하는 방법을 추상화 한다.
DataSource
는 커넥션을 획득하는 방법을 추상화하는 인터페이스이다.public interface DataSource {
Connection getConnection() throws SQLException;
}
대부분의 커넥션 풀은 DataSource
인터페이스를 이미 구현해 두었다. 따라서 개발자는 커넥션 풀의 코드를 직접 의존하는 것이 아니라 DataSource
인터페이스에만 의존하도록 애플리케이션 로직을 작성하면 된다.
커넥션 풀 구현 기술을 변경하고 싶으면 해당 구현체로 갈아 끼우면 되는 방식이다.
JAVA는 DataSource
를 통해 커넥션을 획득하는 방법을 추상화 했다. 이제 애플리케이션 로직은 DataSource 인터페이스만 의존하면된다.
package hello.jdbc.connection;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import static hello.jdbc.connection.ConnectionConst.*;
@Slf4j
public class ConnectionTest {
@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 - 항상 새로운 커넥션을 획득
DriverManagerDataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
// ==> //DataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
useDataSource(dataSource);
//위와 똑같은데 데이터소스 즉 인터페이스를 통해 가져온다는 차이가 있다.
// 사실 비슷해 보이는데 큰 차이가 있다.
// 호출 할때마다 URL, USERNAME, PASSWORD를 입력하지 않아도 된다.
// 설정과 사용의 분리를 이룰 수 있다.
}
DataSource
를 통하여 커넥션을 획득할 수 있다. 기존의 방법과 큰 차이가 있다.DriverManager
는 커넥션을 획득 할 때 마다 URL
, USERNAME
, PASSWORD
같은 파라미터를 계속 전달해야 한다. 반면 DataSource를 사용하는 방식이라면 처음 객체를 생성할 때만 필요한 파라미터를 넘겨두고 단순히 dataSource.getConnection()
만 호출하면 된다.이를 통하여 설정과 사용의 분리가 가능해 지는데, 설정과 관련된 속성들이 한곳에 있는 것이 향후 변경에 더 유연하게 대처할 수 있도록 한다.
굉장히 사소한 차이지만 큰 차이를 만들어내는데, 쉽게 이야기하면 Repository는 DataSource
만 의존한다. 보통 어플리케이션 개발에서 설정은 한 곳에서 하지만 사용은 수 많은 곳에서 하게 되는데 사용과 설정 부분을 명확히 분리해 낼 수 있는 장점을 가진다.
@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");
useDataSource(dataSource);
Thread.sleep(1000);
}
실행결과 :
> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :compileTestJava UP-TO-DATE
> Task :processTestResources NO-SOURCE
> Task :testClasses UP-TO-DATE
> Task :test
16:12:48.916 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - MyPool - configuration:
16:12:48.926 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - allowPoolSuspension................................false
16:12:48.926 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - autoCommit................................true
16:12:48.926 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - catalog................................none
16:12:48.926 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - connectionInitSql................................none
16:12:48.927 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - connectionTestQuery................................none
16:12:48.927 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - connectionTimeout................................30000
16:12:48.927 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - dataSource................................none
16:12:48.927 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - dataSourceClassName................................none
16:12:48.927 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - dataSourceJNDI................................none
16:12:48.928 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - dataSourceProperties................................{password=<masked>}
16:12:48.928 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - driverClassName................................none
16:12:48.928 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - exceptionOverrideClassName................................none
16:12:48.928 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - healthCheckProperties................................{}
16:12:48.929 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - healthCheckRegistry................................none
16:12:48.929 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - idleTimeout................................600000
16:12:48.929 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - initializationFailTimeout................................1
16:12:48.929 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - isolateInternalQueries................................false
16:12:48.930 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - jdbcUrl................................jdbc:h2:tcp://localhost/~/test
16:12:48.930 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - keepaliveTime................................0
16:12:48.930 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - leakDetectionThreshold................................0
16:12:48.930 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - maxLifetime................................1800000
16:12:48.930 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - maximumPoolSize................................10
16:12:48.930 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - metricRegistry................................none
16:12:48.930 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - metricsTrackerFactory................................none
16:12:48.931 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - minimumIdle................................10
16:12:48.931 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - password................................<masked>
16:12:48.931 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - poolName................................"MyPool"
16:12:48.931 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - readOnly................................false
16:12:48.931 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - registerMbeans................................false
16:12:48.931 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - scheduledExecutor................................none
16:12:48.931 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - schema................................none
16:12:48.931 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - threadFactory................................internal
16:12:48.931 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - transactionIsolation................................default
16:12:48.932 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - username................................"sa"
16:12:48.932 [Test worker] DEBUG com.zaxxer.hikari.HikariConfig - validationTimeout................................5000
16:12:48.932 [Test worker] INFO com.zaxxer.hikari.HikariDataSource - MyPool - Starting...
16:12:48.947 [Test worker] DEBUG com.zaxxer.hikari.util.DriverDataSource - Loaded driver with class name org.h2.Driver for jdbcUrl=jdbc:h2:tcp://localhost/~/test
16:12:49.031 [Test worker] DEBUG com.zaxxer.hikari.pool.HikariPool - MyPool - Added connection conn0: url=jdbc:h2:tcp://localhost/~/test user=SA
16:12:49.032 [Test worker] INFO com.zaxxer.hikari.HikariDataSource - MyPool - Start completed.
16:12:49.038 [MyPool connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - MyPool - Added connection conn1: url=jdbc:h2:tcp://localhost/~/test user=SA
16:12:49.038 [Test worker] INFO hello.jdbc.connection.ConnectionTest - connection=HikariProxyConnection@1540894701 wrapping conn0: url=jdbc:h2:tcp://localhost/~/test user=SA, class=class com.zaxxer.hikari.pool.HikariProxyConnection
16:12:49.039 [Test worker] INFO hello.jdbc.connection.ConnectionTest - connection=HikariProxyConnection@203149502 wrapping conn1: url=jdbc:h2:tcp://localhost/~/test user=SA, class=class com.zaxxer.hikari.pool.HikariProxyConnection
16:12:49.134 [MyPool housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - MyPool - Pool stats (total=2, active=2, idle=0, waiting=0)
16:12:49.138 [MyPool connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - MyPool - Added connection conn2: url=jdbc:h2:tcp://localhost/~/test user=SA
16:12:49.141 [MyPool connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - MyPool - Added connection conn3: url=jdbc:h2:tcp://localhost/~/test user=SA
16:12:49.144 [MyPool connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - MyPool - Added connection conn4: url=jdbc:h2:tcp://localhost/~/test user=SA
16:12:49.147 [MyPool connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - MyPool - Added connection conn5: url=jdbc:h2:tcp://localhost/~/test user=SA
16:12:49.150 [MyPool connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - MyPool - Added connection conn6: url=jdbc:h2:tcp://localhost/~/test user=SA
16:12:49.153 [MyPool connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - MyPool - Added connection conn7: url=jdbc:h2:tcp://localhost/~/test user=SA
16:12:49.156 [MyPool connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - MyPool - Added connection conn8: url=jdbc:h2:tcp://localhost/~/test user=SA
16:12:49.159 [MyPool connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - MyPool - Added connection conn9: url=jdbc:h2:tcp://localhost/~/test user=SA
16:12:49.159 [MyPool connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - MyPool - After adding stats (total=10, active=2, idle=8, waiting=0)
BUILD SUCCESSFUL in 2s
4 actionable tasks: 1 executed, 3 up-to-date
4:12:50 PM: Execution finished ':test --tests "hello.jdbc.connection.ConnectionTest.dataSourceConnectionPool"'.
active = 2 , idle = 8
사용준인 커넥션, 대기 상태인 커넥션을 확인 할 수 있다.