WAS와 DB의 연결
WAS와 DB 사이 연결에는 많은 비용이 든다. 만약 하나의 SQL을 DB에 적용한다면 다음과 같은 과정을 거친다.
- JDBC 드라이버 로드
- DB 연결
- statement 생성 (인터페이스이며 DB에 SQL을 전달해주는 역할)
- SQL 전송
- ResultSet을 통해 결과 확인
- 연결 해제
SQL 쿼리 하나를 전송하기 위해 위와 같은 행동이 반복된다. 이는 매우 비효율적이다. 정리하면 다음과 같다!
- 전체과정 중 커넥션을 생성하는 과정이 약 50%를 차지한다고 한다.
- 이러한 연결과정은 TCP/IP를 통해 이루어져 3-way-handshaking 과정을 통해 준비한다.
- 쿼리를 요청할 때 반복되면 네트워크 구간에서 병목현상이 발생할 수 있다.
이를 어떻게 해결할 수 있을까? 바로 커넥션 풀을 사용하는 것이다.
커넥션 풀이란?
WAS와 DB 사이의 커넥션을 이미 맺고 이를 커넥션 풀에서 관리하는 것이다. 이를 통해 네트워크 연결 시간을 단축할 수 있다.
- 커넥션 개수를 일정 수준으로 제한하여 불필요한 자원을 낭비하면 안된다.
- 일관된 데이터베이스 성능을 유지할 수 있다.
HikariCP란?
HikariCP는 용량이 작고 빠른 속도를 가진 JDBC 커넥션 풀 프레임워크이다.
SpringBoot의 default 커넥션 풀로 사용된다.
HikariCP는 어떻게 동작할까?
- Thread 1 이 Connection Pool에 Connection을 요청한다.
- 사용할 수 있는 Connection을 찾는다.
- 있다면 Connection을 반환한다.
- 없다면 없음을 반환한다.
- Thread는 HandOffQueue를 Polling한다. (주기적으로 검사)
- 사용가능한 Connection이 HandOffQueue에 삽입된다.
HikariCP의 기본정보를 알아보자!
- maximumPoolSize
- 풀이 제공할 수 있는 최대 Connection 개수이다. 기본 값은 10개이다.
- TPS(초당 트랜잭션 개수)에 가장 영향을 준다고한다.
- connectionTimeout
- 커넥션 풀에서 커넥션을 구하기 위해 대기하는 시간이다.
- 기본값은 30초이다.
- 보통 웹 서비스는 0.5 ~ 3초 이내로 설정하여 응답 시간을 최소화 한다.
- maxLifeTime
- 커넥션을 생성한 뒤 설정된 시간이 지나면 커넥션을 닫고 풀에서 제거한다. 제거한 뒤 커넥션을 새롭게 생성한다. 기본값은 30초이다.
- 기본적인 규칙은 네트워크나 데이터베이스의 관련 설정 값보다 작은 값을 사용한다.
- 최대 TCP 유지 시간이 10분이라고 가정한다. 이 값이 관련 설정보다 크게되면 이미 유효하지 않은 커넥션이 풀에 남게 된다.
- keepaliveTime
- 커넥션이 살아 있는지 확인하는 주기이다. 유휴 커넥션에 대해 커넥션을 검증하고 유효하지 않은 경우 풀에서 제거한다. 제거 후 커넥션을 새로 생성한다.
- 데이터베이스의 미활동 시간보다 설정 시간이 긴 경우 이미 데이터베이스에서 커넥션을 제거했기 때문에 무의미한 검증 시간이 될 수 있다.
- minimumIdle
- 풀에서 유지할 최소 유휴 커넥션 개수를 지정한다. 기본 값은 maximumPoolSize와 동일하다.
- HikariCP 문서에 따르면 설정하지 않는 것을 추천한다. 즉 maximumPoolSize와 동일 크기를 추천한다. 이 값을 작게 설정할 경우 급격한 트래픽 증가 시 성능 저하를 일으킬 수 있다.
적절한 커넥션 풀 사이즈를 고민해보자
커넥션 풀을 구성하는 것은 개발자가 자주 실수하는 것이다. 풀을 구성할 때 이해 해야하는 몇 가지 원칙이 있을 수 있다.
하나의 CPU 코어가 있는 컴퓨터도 수십 혹은 수백 개의 스레드를 동시에 지원할 수 있다. 하지만 이것은 운영체제의 속임수일 뿐이다. 실제로 단일 코어는 한 번에 하나의 스레드만 실행할 수 있다. 운영체제는 컨텍스트 스위칭을 한 뒤 다른 스레드의 코드를 실행할 뿐이다. 즉 빠른 시간의 컨텍스트 스위칭으로 동시에 진행하는 것 처럼 보일 뿐이다.
단일 CPU가 주어지면 A와 B를 순차적으로 실행하는 것이 시분할을 통해 A와 B를 동시에 실행하는 것 보다 항상 빠를 것이라는 것은 컴퓨팅의 기본 법칙이다. 스레드 수가 CPU 코어 수를 초과하면 단순히 스레드 수가 더 많아질 뿐이지 더 빠른 속도를 보장하는 것은 아니다. 즉 단순히 풀의 크기를 늘린다고 더 빠른 속도로 처리되는 것은 아니다.
데이터베이스의 주요 병목 현상은 다음과 같다.
- CPU
- 디스크
- 네트워크
- 여기서, 디스크와 네트워크를 무시하면 간단히 계산이 가능하다. 8개의 CPU 코어가 있다면, 커넥션 수를 8개로 설정하면 최적일 것이다.
- DB는 일반적으로 디스크에 저장된다. (아래 과정이 일어나는 동안 스레드는 block 한다)
- 전통적인 모터 구동 암에 읽기/쓰기 헤드가 장착된 회전 금속 플레이트로 구성된다.
- 읽기/쓰기 헤드는 한 번에 한 곳에만 읽을 수 있으며 다른 쿼리에 대한 데이터를 읽기 위해서는 새 위치를 검색해야 한다.
- 탐색 시간 비용과 플래터의 데이터가 다시 돌아오기 까지 디스크를 기다려야 하는 회전 비용이 추가적으로 발생한다.
- 네트워크는 이더넷 인터페이스를 통해 유선으로 데이터를 작성하면 송/수신 버퍼가 가득차거나 멈출 때 block이 발생할 수 있다.
적절한 사이즈 공식을 적용해보자
PostgreSQL에서 제공한 공식
다양한 상황을 고려 했을 때 PostgreSQL에서는 아래와 같은 공식을 제안했다.
여러 데이터베이스에도 적용할 수 있다고 언급되어 있다.
connections = (corecount * 2) + effectivespindle_count
- core_count * 2: 코어 수에 근접할 수록 좋지만, 위에서 언급한 디스크 및 네트워크와 CPU의 속도차이로 인한 여유 시간을 활용하기 위해 계수 2를 곱해준다.
- effective_spindle_count: 하드 디스크는 하나의 spindle을 가진다. spindle은 데이터베이스 서버가 관리할 수 있는 동시 I/O 요청 수를 말한다. 디스크가 n개 존재하면 spindle_count는 n이 될 수 있다.
참고
https://github.com/brettwooldridge/HikariCP#gear-configuration-knobs-baby
https://code-lab1.tistory.com/m/209
https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html
https://woowabros.github.io/experience/2020/02/06/hikaricp-avoid-dead-lock.html
https://hyuntaeknote.tistory.com/12
'공부 > Spring' 카테고리의 다른 글
KAFKA 알아보기(By Spring) (0) | 2023.05.08 |
---|---|
객체지향과 의존성 (0) | 2022.11.01 |
QueryDSL에서 수정, 삭제 벌크연산 하는방법을 알아보자 (0) | 2022.08.26 |
[QueryDSL] 동적쿼리를 해결해보자 (0) | 2022.08.26 |
[QueryDSL] 프로젝션(SELECT 대상)에 따라 다른 결과를 가져와 보자 (0) | 2022.08.26 |
댓글