[DB] DB드라이버, 커넥션 풀, DataSource

2022. 6. 19. 18:42[ Basic ]/# 데이터베이스

애플리케이션에서 데이터베이스와 커넥션을 생성하는 과정

 

 

1) 애플리케이션 로직은 DB 드라이버(어떤 DB를 사용할 것이냐에 따라 다름)를 통해 커넥션을 조회한다.

2) DB 드라이버는 DB와 TCP/IP 커넥션을 맺는다. (3-way handshake 발생)

3) TCP/IP 커넥션이 완료되면 DB 접속을 위한 ID 및 Password 등과 같은 정보를 DB에 전달한다.

4) DB는 사용자 인증을 완료하면 DB 내부적으로 세션을 만든다.

5) DB는 커넥션 생성이 완료되었다는 응답을 보낸다.

6) DB 드라이버는 애플리케이션 로직으로 커넥션 객체를 반환한다.

 

 

단점

- 커넥션을 생성하는 것은 복잡하고 시간도 오래 소요되는 과정이다.

- 시간이 오래 걸리기 때문에 사용자에게 느린 응답을 보일 수밖에 없다.

- 매번 WAS에서 TCP/IP 커넥션을 생성하는 것은 리소스 소모가 크다. 

 

 


 

커넥션 풀

커넥션 풀은 위와 같이 매번 TCP/IP를 맺고 끊는 방식의 단점을 해결하고자 적용된 것이다. 커넥션 풀은 애플리케이션이 시작할 때 미리 커넥션을 생성해두고 재사용하는 방식이다. 따라서 커넥션을 맺을 필요 없이 '즉시' 쿼리를 전달할 수 있다.

 

 

- 애플리케이션 로직에서는 DB 드라이버를 통해 커넥션을 획득할 필요 없다.

- 커넥션 풀을 통해 이미 생성되어 있는 커넥션을 가져다 쓰는 방식이다.

- 애플리케이션 로직은 커넥션 풀에서 받은 커넥션을 기반으로 쿼리를 수행하면 된다.

- 커넥션을 다 사용한 뒤에는 커넥션 풀에 반환해야 한다.

- 커넥션을 반환할 때에는 커넥션을 종료하지 않고 살아있는 상태로 그대로 반환해야 한다.

 

 

장점

- 미리 생성된 커넥션을 사용하기 때문에 쿼리를 즉시 실행할 수 있고 따라서 사용자 입장에서는 빠른 응답을 기대할 수 있다.

- 커넥션 풀이 최대 커넥션 개수를 애플리케이션이 시작할 때 미리 만들기 때문에(최대 커넥션 수 제한) 커넥션이 기하급수적으로 늘어나는 것을 방지할 수 있다.

- 커넥션 풀을 구현한 다양한 오픈소스들이 있다.(HikariCP 등)

 

 

단점

- 커넥션 풀에 최대로 저장되는 커넥션 수는 정해져(제한되어) 있기 때문에 많은 요청이 발생한 경우 커넥션이 모두 사용 중이라면 반납될 때까지 대기해야 한다.

- 커넥션 객체는 메모리를 많이 사용하기 때문에 풀 크기를 크게 둔다면 오히려 성능을 떨어뜨릴 수 있다.

- 미리 연결된 커넥션을 풀에 담아두는 형태이기 때문에 커넥션 요청이 많지 않을 경우 불필요한 리소스를 많이 잡아두게 될 수도 있다.

 

 

[참고]

참고1) 커넥션 풀에서 관리되던 커넥션의 TCP 만료가 발생했다면 어떻게 될까?

- 커넥션 풀 구현체마다 다르지만 백그라운드에서 주기적으로 TCP 타임아웃을 체크하는 쓰레드를 두는 식으로 해결할 수 있다. HikariCP의 경우에는 커넥션 획득 요청이 왔을 때 해당 커넥션의 TCP 상태를 확인하는(실제 DB로 요청을 보내서 확인) 과정을 거친다. 

참고2) MySQL의 공식문서에서는 600여 명의 유저를 대응하는데 15~20개의 커넥션 풀만으로도 충분하다고 언급하고 있다.

참고3) 커넥션 풀이 필요 없는 경우: 데이터베이스 스케줄링 서비스, 특정 시간에만 특정 커넥션을 사용할 경우

 


 

 

DataSource

DB 드라이버를 통해 직접 커넥션을 가져오던 방식에서 HikariCP와 같은 커넥션 풀을 사용하도록 변경할 경우, 애플리케이션 코드 자체도 변경해야 한다는 단점이 있다. 이러한 문제를 해결하고자 '커넥션을 획득하는 과정' 자체를 추상화하기 시작했다.

 

javax.sql.DataSource : 자바 표준 인터페이스

https://docs.oracle.com/javase/8/docs/api/javax/sql/DataSource.html

 

위와 같이 커넥션을 획득하는 과정을 추상화하고자 자바에서 DataSource라는 표준 인터페이스를 제공한다. DataSource 인터페이스는 커넥션을 획득하는 방법을 추상화한다. 따라서 DataSource 인터페이스는 커넥션을 조회하는 기능을 핵심으로 다룬다.

 

 

대부분의 커넥션 풀은 DataSource 인터페이스를 구현해두었다. 따라서 커넥션 풀 구현체를 바꾸더라도 인터페이스에 의존하기 때문에 코드를 변경하지 않아도 된다. 하지만 DB 드라이버(Driver Manager)는 DataSource를 구현하지 않았다. 따라서 DB 드라이버를 사용하려면 관련 코드를 변경해야 한다. 이러한 문제를 해결하고자 스프링에서는 DataSource를 구현하며 DB 드라이버를 확장한 DriverManagerDataSource 구현체를 제공한다. 하지만 DriverManagerDataSource는 커넥션 풀을 사용하지 않고 매번 커넥션을 새로 생성하는 방식이다. 결론적으로 DB 드라이버를 사용하든 커넥션 풀을 사용하든 DataSource 인터페이스에만 의존할 수 있게 된 것이다.

 

 

cf. DataSource의 메소드를 보면 JDBC URL을 지정하지 않고 커넥션을 가져온다는 점이 특징이다. 설정과 사용을 분리함으로써 설정과 관련된 속성들에 대한 변경에 유연하게 대응할 수 있다.

 


 

cf. HikariCP

JDBC URL, maximumPoolSize 등을 설정할 수 있다. 커넥션을 만들고 커넥션 풀에 커넥션을 첨가하는 작업은 별도의 쓰레드에서 진행된다. 커넥션을 만드는 과정은 TCP/IP 통신이 필요하기 때문에 순서대로 실행된다면 애플리케이션이 로드되는데 오랜 시간이 걸릴 것이다. 따라서 별도의 쓰레드에서 커넥션을 만들고 커넥션 풀에 채운다.

 

또한 HikariCP를 사용할 때 커넥션 풀에 커넥션이 없다면 신규 커넥션이 들어올 때까지 DataSource의 getConnection 메소드는 대기한다(Blocked). 여기서 커넥션 풀에 커넥션이 반납되기를 기다리는 시간을 설정할 수 있다.(HikariCP 공식문서 참고)

 

 

cf. HicariCP의 connectionTimeout 설정 정보

This property controls the maximum number of milliseconds that a client (that's you) will wait for a connection from the pool. If this time is exceeded without a connection becoming available, a SQLException will be thrown. Lowest acceptable connection timeout is 250 ms. Default: 30000 (30 seconds)

 

참고로 애플리케이션 쓰레드에서 커넥션을 사용하던 중에 TCP 타임아웃 발생 등으로 커넥션이 끊어지면 오류가 발생한다. 이와 관련하여 설정할 정보들은 hikaricp 최적화 관련 설정을 참고하면 된다.

 

 

Reference

- HikariCP 공식문서, https://github.com/brettwooldridge/HikariCP

- JDBC timeout, https://d2.naver.com/helloworld/1321