커넥션풀과 데이터소스 이해
커넥션풀이 왜 필요할까?
아래의 코드와 같이 데이터베이스에 접근하는 커넥션을 생성하여 가져올 때 DriverManager.getConnection()을 호출하면 된다.
public static Connection getConnection(){
try {
Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
log.info("get connection = {}, class = {}", connection, connection.getClass());
return connection;
} catch (SQLException e) {
throw new IllegalStateException();
}
}
- 이 코드를 호출함으로써 어떠한 작업이 일어날까?
- 라이브러리의 데이터베이스 목록을 조회하여 URL로 각 데이터베이스에 맞는 패턴인지 확인한다.
- 패턴이 일치한다면 해당 데이터베이스로 TCP/IP 연결 요청을 한다.(3way handshake)
- TCP/IP 연결이 완료된다면 아이디 패스워드로 로그인 요청을 한다.
- 데이터베이스 서버는 세션을 생성하고 접속을 허가해준다.
- 애플리케이션 서버는 데이터베이스에 접근하여 작업을 수행한다.
- 작업이 완료되면 커넥션 등의 리소스를 반환하고 TCP/IP 연결을 종료한다.
- 위의 일련의 과정들은 트래픽이 많은 환경에서는 꽤나 큰 비용이라고 볼 수 있다.
이러한 문제를 해결하기 위해 애플리케이션이 시작할 때 미리 데이터베이스의 커넥션을 여러개 만들어두고(기본 값은 보통 10개) 이를 재사용하는 방식을 사용한다. 이를 커넥션 풀이라고 한다.
- 대표적인 커넥션풀로는 HikariCP가 있으며 Spring을 사용하면 디폴트로 사용하게 된다.
DataSource
- 데이터 소스는 커넥션을 얻어오는 방법을 추상화한 것이다.
- 이 데이터소스를 사용하면 우리는 커넥션 풀에서 커넥션을 가져오든 DriverManager를 통해 커넥션을 가져오든 구현체를 주입해 사용하면 된다.
- 이 말은 어플리케이션 코드는 수정하지 않아도 된다는 이야기다!!
- 아래의 두 그림을 비교해보자
- 데이터소스라는 인터페이스만 의존하여 커넥션을 가져오면 커넥션을 획득하는 방법을 바꿔도 애플리케이션의 로직을 수정하지 않아도 된다.
DriverManagerDataSource vs HikariCP 커넥션 풀
이제 DataSource를 상속하는 두 구현체를 살펴보자. 둘 다 커넥션을 획득하는 역할을 수행하는데 차이점은 요청시 커넥션을 매번 생성하느냐 애플리케이션 실행시 풀을 만들어서 커넥션을 재사용하느냐이다.
Member member = new Member("memberV100", 10000);
repository.save(member);
//findById
Member findMembmer = repository.findById(member.getMemberId());
log.info("findMember={}", findMembmer);
assertThat(findMembmer).isEqualTo(member);
//update
repository.update(member.getMemberId(), 20000);
Member updatedMember = repository.findById(member.getMemberId());
assertThat(updatedMember.getMoney()).isEqualTo(20000);
//delete
repository.delete(member.getMemberId());
- 위와 같은 코드가 순차적으로 실행된다고 생각해보자.
- Repository의 메서드를 접근하고 마칠때마다 connection을 얻고 close()하는 작업을 반복한다면 두 데이터소스 구현체에서는 어떻게 동작할까?
- DriverManagerDataSource (매번 커넥션 연결)
get connection=conn0: url=jdbc:h2:.. user=SA class=class
org.h2.jdbc.JdbcConnection
get connection=conn1: url=jdbc:h2:.. user=SA class=class
org.h2.jdbc.JdbcConnection
get connection=conn2: url=jdbc:h2:.. user=SA class=class
org.h2.jdbc.JdbcConnection
get connection=conn3: url=jdbc:h2:.. user=SA class=class
org.h2.jdbc.JdbcConnection
get connection=conn4: url=jdbc:h2:.. user=SA class=class
org.h2.jdbc.JdbcConnection
get connection=conn5: url=jdbc:h2:.. user=SA class=class
org.h2.jdbc.JdbcConnection
- HikariCP (커넥션 재사용)
get connection=HikariProxyConnection@xxxxxxxx1 wrapping conn0: url=jdbc:h2:...
user=SA
get connection=HikariProxyConnection@xxxxxxxx2 wrapping conn0: url=jdbc:h2:...
user=SA
get connection=HikariProxyConnection@xxxxxxxx3 wrapping conn0: url=jdbc:h2:...
user=SA
get connection=HikariProxyConnection@xxxxxxxx4 wrapping conn0: url=jdbc:h2:...
user=SA
get connection=HikariProxyConnection@xxxxxxxx5 wrapping conn0: url=jdbc:h2:...
user=SA
get connection=HikariProxyConnection@xxxxxxxx6 wrapping conn0: url=jdbc:h2:...
user=SA
스프링을 사용하면 기본적으로 HikariCP를 사용하게 된다. (기본 풀 사이즈 = 10)
Reference
- 스프링 DB 1편 - 데이터 접근 핵심 원리 by 김영한
'DB' 카테고리의 다른 글
Real MySQL 9장 옵티마이저와 힌트 #1 (~9.2) (0) | 2022.06.21 |
---|---|
Real MySQL 8장 인덱스 #2 (8.6~) (0) | 2022.06.10 |
JDBC란? (SQL Mapper, ORM) (0) | 2022.06.03 |
Real MySQL 8장 인덱스 #1 (~8.5) (0) | 2022.05.27 |
Real MySQL 6장 데이터압축, 7장 데이터 암호화 (0) | 2022.05.26 |