서론
데이터베이스 커넥션을 획득하기 위해서는 아래와 같은 복잡한 단계를 거치게 된다.
- 커넥션을 맺을 수 있는 DB 드라이버(H2, MySQL 등)를 찾는다.
- DB 드라이버와 3-way-handshake 를 통해 TCP/IP 연결을 맺는다.
- ID, PW와 기타 부가정보를 DB 전달하고, DB는 전달받은 ID,PW 를 통해 내부 인증을 완료하고 DB 세션을 생성한다.
- DB는 커넥션이 생성됬임을 알리고, DB 드라이버는 커넥션 객체를 생성해서 클라이언트에게 반환한다.
오버헤드가 너무 큰데??.... 맞다!!
데이터베이스와 커넥션을 맺는것은 복잡하고 시간도 오래 걸린다.
그럼 미리 커넥션을 만들고 필요할때 주면 되는거 아니야??... 맞다!!
어플리케이션 로딩 시점에 필요한 만큼의 커넥션을 미리 생성해둔 후, 사용하는 것이 커넥션 풀이다.
커넥션 풀
커넥션 풀에 있는 커넥션들은 어플리케이션 로딩 시점에 TCP/IP 연결이 되어있는 상태이기 때문에 언제든지 즉시 SQL을 DB에 전달할 수 있다.
다시 말해, 커넥션이 필요할 때 DB 드라이버를 통해 새로운 커넥션을 얻는 것이 아니라 커넥션 풀에 이미 생성된 커넥션 객체를 참조로 그냥 가져다 쓰면 되는 것이다.
또한, 사용을 마친 커넥션은 종료시키는 것이 아니라 다시 사용할 수 잇도록 커넥션 풀에 반환하게 된다.
그럼 커넥션 풀의 커넥션 수가 많을수 록 좋은 거아니야??!
물론 커넥션 풀을 크게 하면 그만큼 줄 수 있는 커넥션의 양이 많아지고, 재사용 가능한 커넥션 수가 많아져 더 많은 요청을 처리하는 것이 가능하다.
하지만 커넥션 풀이 커질수록 커넥션을 보관할 메모리의 소모가 커지고 이에 맞는 DB 스펙도 증가해야 된다.
또한 커넥션을 사용하는 것은 결국 스레드(thread)이다. 어플리케이션의 스레드 풀보다 커넥션 풀이 많다면, 남는 커넥션은 메모리만 차지하고 정작 사용되지 못해 성능만 잡아먹는 경우가 발생할 것이다.
그럼 스레드 풀보다는 적게하고 커넥션 풀을 구성하고, 커넥션 풀이랑 스레드 풀을 둘다 키우면 성능이 더 좋겠네??!
스레드 풀의 용도는 많은 동시 사용자의 요청을 처리하기 위함이고, 커넥션 풀의 용도는 커넥션을 맺는 비용을 단축하기 위해서이다.
DB 작업은 대부분 짧은 시간안에 완료되기 때문에, 하나의 커넥션을 여러 스레드가 공유하여 사용하는 것이 가능하다.
커넥션 풀을 늘리는 것은 메모리 소비와 DB 스펙 증가와 같은 문제가 있기 때문에, 스레드 풀과 커넥션 풀을 서비스의 규모에 맞는 적절한 비용으로 효율적으로 사용할 수 있는 사이즈를 맞추는 것이 중요하다.
그러므로 커넥션 풀을 무작정 늘린다고 성능이 증가하는 것이 아니다.
그럼 커넥션 풀 방식은 어떻게 사용할까??
그리고 직접 DriverManager를 이용한 커넥션을 얻고자 하려면, 기존 커넥션 풀 방식 코드를 다 바꿔야 할까?
아니다! 이러한 문제를 해결하고자 java에서 DataSource라는 커넥션 획득방법을 추상화한 인터페이스를 제공해준다.
데이터소스(DataSource)
DataSource 인터페이스를 구현한 구현체로는 커넥션 풀 방식의 HikariCp, DBCP2 등이 있고, 직접 커넥션을 맺는 방식의 스프링이 제공하는 DriverManagerDataSource가 있다.
즉, 어플리케이션은 DataSouce 인터페이스에 의존하고 있기 때문에 필요에 따라 구현체만 바꿔 끼우면 되는 것이다.
그럼 코드를 통해 새로운 커넥션을 가져오는지, 재사용하는 지를 직접 보도록 하자
DriverManagerDataSource
매번 새로운 커넥션을 가져오는 것을 보기 위해, 첫번째 얻은 커넥션을 반환한 뒤 다시 커넥션을 가져오도록 했다.
과연 새로운 커넥션을 가져올까?
결과와 같이 항상 새로운 커넥션을 가져오는 것을 볼 수 있다.
참고로 직접 DriverManager를 통해 커넥션을 만들고 가져오기 때문에, 반환 시 해당 커넥션은 재사용되지 못한다.
HikariCp
커넥션 풀의 구현체인 HikariCP를 이용하여 테스트 해보았다.
마찬가지로 커넥션을 재사용하는 지를 보기 위해, 첫번째 얻은 커넥션을 반환 후 새로운 커넥션을 가져오고록 해보았다.
과연 커넥션을 재사용할까?
결과를 보게 되면 첫번째 가져온 커넥션을 두번째에서도 재사용되는 것을 볼 수 있다.
즉, DriverManager방식과 달리 커넥션 풀 방식에서는 반환한 커넥션을 커넥션 풀로 반환하게 되고 재사용되게 된다.
그럼 만약 커넥션 풀의 남는 커넥션이 없으면??
커넥션 풀의 size를 10으로 설정하고 테스트 해보았다.
결과와 같이 time out 이 걸리게 되고 연결이 종료되는 것을 볼 수 있다.
이와 같이 동시 여려 요청의 쓰레드가 들어오게 되면 커넥션 풀에 남아있는 커넥션을 주게 되고, 만약 남은 커넥션이 없다면 해당 스레드는 Block 상태가 되게 된다.
자료
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard
'JAVA > Spring & Java 학습 기록' 카테고리의 다른 글
동시성 이슈와 데드락 문제를 비관적 락를 이용해 해결하다. (0) | 2023.06.28 |
---|---|
스프링의 예외 누수 문제 해결 변천사 (0) | 2023.06.25 |
스프링 인터셉터를 이용한 권한 검증 분리하기 (0) | 2023.06.14 |
스프링 트랜잭션 AOP 등장까지의 변천사 (0) | 2023.05.27 |
트랜잭션 이해와 락 (0) | 2023.05.24 |