개요
Spring을 이용해 서버 개발을 할 때, DB를 이용하지 않는 순간은 거의 없을 것입니다.
그만큼 DB는 서버 개발에 있어서 필수적인 존재입니다.
하지만, Spring은 Java를 사용하고 MySQL과 같은 DBMS는 SQL을 이용합니다.
따라서 둘 간의 연결을 위한 인터페이스가 필요합니다.
Java진영에서는 DBMS와의 연결을 간단하게 하기 위해 JDBC를 도입했습니다.
JDBC를 이용하면, DBMS에게서 간단하게 Connection을 얻어와 쿼리와 데이터를 주고받을 수 있습니다.
(JDBC에 대한 글 추가예정)
DBMS측은 Java와 같은 언어에서 DBMS에 쉽게 접근하기 위해 Connector를 준비해두고 있습니다.
https://www.mysql.com/products/connector/
MySQL :: MySQL Connectors
MySQL Connectors MySQL provides standards-based drivers for JDBC, ODBC, and .Net enabling developers to build database applications in their language of choice. In addition, a native C library allows developers to embed MySQL directly into their applicatio
www.mysql.com
대표적으로, MySQL은 위의 링크에서 각 언어에 대한 Connector를 다운로드받고 설치할 수 있습니다.
그러나, 위의 JDBC처럼 Connection을 자유롭게 사용하게 두는 것은 문제를 일으킬 수 있습니다.
1. Connection이 제대로 관리되지 않는 경우
Request를 처리할 때마다 Connection을 늘린다면?
수많은 Connection이 발생할 것이고, 시스템에 부하가 발생할 것입니다.
2. Connection그 자체의 비용
Connection을 Request마다 만들고 제거하는 것은 비용이 발생합니다.
우리는 이와 같은 문제를 겪은 적이 있습니다.
Spring에서 Client로부터 Request를 받을 때, Spring은 Thread로 해당 Connection을 관리합니다.
하지만, 무작정 Request를 받게 되면 Thread가 늘어날 것이고...
이 때문에 Tomcat은 Thread Pool을 도입해 이러한 문제를 해결했습니다.
(Spring에서 Thread Pool - https://nangmandeveloper.tistory.com/10)
두 가지 경우 이외에도 많은 문제가 있을 것이고, 자원을 직접 관리하는 데 있어 문제점도 발생할 것입니다.
때문에 Spring에서는 DB Connection을 관리하기 위해 DBCP(DataBase Connection Pool)개념을 도입했습니다.
Connection Pool
Connection Pool의 개념은 간단합니다.
Thread Pool처럼, DB와의 Connection개수를 일정량 사이에서 조절하는 것입니다.
과도한 Connection의 생성을 막을 수 있고(비용 절감), 최소의 Connection Pool을 유지함으로 반응성을 높일 수 있고 오버헤드를 줄일 수 있습니다.(성능)
위의 그림처럼, JDBC는 Connection Pool을 이용해 DBMS와의 통신을 이어갑니다.
그림에서는 3개의 Connection을 유지하고 있습니다.
Thread Pool처럼, Connection Pool은 최소, 최대 Connection을 가지고 있을 것입니다.
만약 min Connection이 3이라면, 최소 3개의 Connection을 유지합니다.
Request가 들어오기 시작해 Connection이 부족해질 때가 올 것입니다.
그렇다면, Connection Pool은 ThreadPool처럼 Connection을 늘립니다.
max Connection이 10이라면, 최대 10개의 Connection까지 늘릴 것입니다.
다만, 10개의 Connection이 생겼음에도 불구하고 Request가 몰린다면, 해당 Request를 대기시키고 순차적으로 처리합니다.
JDBC
Connection Pool은 JDBC내부에 있는 구조입니다.
JDBC API를 사용하기 위해서는 먼저 JDBC Driver를 로딩하고 DBMS에 대한 정보를 입력해야 합니다.
JDBC Driver는 DBMS와 연결되고, JDBC DriverManager에 의해 관리됩니다.
사용자는 DriverManager를 이용해 각 DBMS와의 Connection을 받고 사용할 수 있습니다.
JDBC를 이용한다면, 위와 같은 과정을 거쳐서 Connection을 얻어오고 데이터를 객체에 입력해 사용할 것입니다.
JDBC는 DataSource 인터페이스를 이용해 DriverManager로부터 Connection을 얻어오는 과정을 추상화했습니다.
덕분에, 우리는 getConnection()이라는 일관된 방법으로 DBMS의 종류, Connection을 얻어오는 과정에 상관없이 DB와의 Connection을 ConnectionPool에서 가져오고 사용할 수 있습니다.
이를 사용하기 위해서는, maven이나 graddle에 jdbc, dbms connector설정을 한 뒤 datasource를 입력하면 됩니다.
Hikaricp
Connection Pool을 관리하는 기법은 여러 가지가 있을 것입니다.
이러한 방법은 Thread Pool에서도 많이 찾아볼 수 있습니다.
대표적으로, Tomcat에서 사용하는 ThreadPool과 Virtual Thread에서 Platform Thread를 관리하는 Forkjoin Pool이 있습니다.
(Spring의 ThreadPool - https://nangmandeveloper.tistory.com/10)
(Virtual Thread - https://nangmandeveloper.tistory.com/6)
위의 그림에서 볼 수 있듯이, 많은 종류의 Connection Management Framework가 있습니다.
SpringBoot 2.0에서는 tomcat CP를 사용했지만, SpringBoot 3.0이상부터는 Hikari를 사용하고 있습니다.
이유는 아마 위와 같은 월등한 성능이 아닐까 싶습니다.
SpringBoot를 사용할 때 HikariCP를 튜닝하기 위해서, 위와 같이 application을 수정하면 됩니다.
maximum-pool-size는 CP가 최대로 가지는 Connection 개수, minimum은 최소가 될 것입니다.
keepalive time은 얼마나 연결을 유지하고 있을지, max-lifetime은 connection의 최대수명을,
idle-timeout은 Connection이 유휴 상태에서 얼마나 대기 후 사라질 것이지, connection-timeout은 Request가 Connection을 얻기 위해 얼마나 대기할지...
위의 설정을 통해 서버 환경에 따라 HikariCP를 튜닝할 수 있습니다.
또, HikariCP는 아래처럼 작업을 처리합니다.
1. Request가 이전에 사용한 Connection중 Idle상태가 있는가
2. 전체 Connection중 Idle상태가 있는가
3. HandoffQueue에 Idle Connection이 있는가
1, 2, 3번 중 하나라도 Yes라면, 해당 Connection을 사용합니다.
그렇지 않는다면 HandoffQueue에서 Connection_timeout만큼 기다립니다.
이 때, Connection_timeout을 초과하면 예외가 발생합니다.
4. 작업 처리
5. HandoffQueue에서 기다리는 task가 있는지 확인
5-1. 있으면 HandoffQueue에 Connection 반환
5-2. 없으면 Request ThreadLocal에 방금까지 사용한 Connection 기록
마치며
서버 개발자는 많은 처리량을 적은 자원으로 처리하기 위해 많은 것을 알아야 한다 생각합니다.
웹 백엔드를 공부하면서 단순히 DB를 사용하기 위해서는 JDBC, Mybatis와 같은 의존성을 추가하고 설정만 해주면 되었습니다. 하지만, DB와 같은 I/O작업을 처리하거나 시간이 오래 걸리는 API등을 처리할 때 생기는 문제점을 겪었었고, 해당 문제점을 해결하기 위해 로우레벨 로직을 직접 보는 경우가 종종 있었습니다.
이러한 문제점은 application에서 HikariCP를 튜닝함으로 간단하게 끝날 수도 있지만, SpringBoot에서 Request를 처리하는 방법, JDBC, Mybatis와 같은 SQL Mapper, JPA와 같은 ORM의 작동구조를 알아야 해결할 수 있는 부분도 존재합니다.
시간이 될 때마다 발생했던 문제점, 해결하려 시도했던 방안 등을 포스팅하도록 하겠습니다.
'WEB > Spring' 카테고리의 다른 글
[Spring] CaffeineCache 사용해보기, 성능테스트 (0) | 2024.04.07 |
---|---|
[Spring] Virtual Thread, DB IO테스트시 발생했던 문제 - 1 (1) | 2024.01.21 |
[Spring] Actuator (1) | 2024.01.04 |
[Spring] SpringBoot에서의 Virtual Thread (2) | 2023.12.31 |
[Spring] Spring의 ThreadPool (1) | 2023.12.31 |