개요
이전에 Virtual Thread, Spring에서 Virtual Thread의 사용에 대해 작성한 포스트가 있었습니다.
(Java21 Virtual Thread : https://nangmandeveloper.tistory.com/6)
(Springboot에서 Virtual Thread사용 : https://nangmandeveloper.tistory.com/11)
해당 글에서 가상 스레드를 사용하는 법, Spring에서 가상 스레드를 사용하는 방법을 소개했습니다.
특히, Springboot에서 가상 스레드를 사용하는 경우, blocked상태가 적은 경우 일반 thread가 효율적이며 blocked상태가 많이 발생하는 경우 가상 스레드의 처리량이 더 높다는 부분을 확인할 수 있었습니다.
그렇다면, thread가 언제 blocked상태가 될까요?
1. 여러 스레드들이 같은 공유자원에 접근할 때.
2. 외부 API로부터 request를 보낸 후, response를 기다릴 때.
3. 내부 작업의 처리시간이 길 때.
4. DB 서버로부터 request를 보낸 후, response를 기다릴 때.
...
이외의 경우도 매우 많으나, 대표적으로 4가지를 꼽아봤습니다.
위의 4가지는 Spring 서버를 만들고 이용할 때 가장 많이 겪게 되는 상황입니다.
Springboot에서 Virtual Thread사용하기 글에서 들었던 blocked사례는 2번, 3번이었습니다.
외부 API로부터 response에 시간이 걸린다, 내부 작업으로부터의 결과를 받는데 시간이 걸린다.
이러한 부분을 메서드에 sleep으로 표현했고, 가상 스레드를 사용함으로 인해 톰캣의 제한된 스레드보다 많은 작업을 처리할 수 있음을 보였습니다.
그렇다면, 4번인 DB서버와의 통신 때에도 같은 원리가 적용될까요?
궁금했던 상황(시간이 걸리는 DB작업)
이전에 조회에 시간이 걸리는 쿼리에 대한 요청을 처리한 적이 있었습니다.
네비게이션 API를 통해 얻어온 경로마다 DB에 등록된 장소들의 거리를 계산하여 반환하는 쿼리였습니다.
간단한 토이프로젝트였기 때문에 최적화를 제대로 해주지 않았기 때문에, 경로마다 모든 지역을 계산했습니다.
그 결과, 한 경로에 대한 쿼리 처리 시간이 5~10초가 걸리는 일이 발생했습니다.
물론 지금이라면 경로 압축, 검색장소 필터링 등을 통해 최적화를 진행했을 것입니다.
하지만 당시 토이프로젝트 구현 시간이 너무 촉박하여 건너뛰었었는데, 이러한 경우 Virtual Thread를 이용해 여러 요청을 한번에 처리할 수 있을지 궁금해졌습니다.
그래서 쿼리에 sleep(1)을 추가하여 시간이 걸리는 DB작업임을 가정하고 테스트를 진행했습니다.
DB, Sleep이 없을 때 환경설정
쿼리에 Sleep이 없는, 단순한 데이터를 요청하는 경우를 먼저 시험했습니다.
테스트 환경
SpringBoot - 3.2.1 버전
DB - MySQL 8.0.33 버전
CPU - i5 7300HQ
RAM - 12GB
IDE - Intellij IDEA Ultimate
OS - Windows 10 64bit
Test tool - Jmeter
SQL Mapper - Mybatis
DB에 저장된 단순한 회원 정보입니다.
pw, account는 암호화되어 있는 것을 볼 수 있습니다.
해당 정보를 조회하기만 하는 쿼리를 실행할 것입니다.
id가 test인 회원의 모든 정보르 usermanage 테이블에서 조회하는 select문입니다.
mybatis mapper interface에 작성되었습니다.
Service layer의 메서드입니다.
Controller layer에서 HTTP Request를 처리하는 메서드입니다.
controller requestmapping을 stress로 두었기 때문에, stress/nosleep경로로 접근하면 유저의 데이터를 반환할 것입니다.
DB, Sleep이 없을 때 결과
Jmeter 설정
Number of Threads : 2000
Ramp-up period : 1
Loop Count : 60
1. Normal Thread
2. Virtual Thread
같은 양의 테스트를 진행했음에도 불구하고, 일반 스레드는 38초, 가상 스레드는 45초가 소모됨을 볼 수 있습니다.
즉, Blocked가 거의 없는 상황에서는 Normal Thread가 유리한 것입니다.
SpringBoot에서 테스트를 진행했을 때에도, Blocked가 없는 상황에서 Normal Thread의 처리량이 우세함을 보았습니다.
(Springboot에서 Virtual Thread사용 : https://nangmandeveloper.tistory.com/11)
그렇다면, DB 작업이 오래 걸리는 상황에서는 어떨까요?
DB, Sleep이 있을 때 환경설정
DB Sleep이 없을 때와 다른 것은 하나뿐입니다.
Select 쿼리에 Sleep(1)이 추가된 것입니다.
해당 쿼리를 사용하면, DB조회시 시간을 지연시킬 수 있습니다.
반환하는 데이터는 같습니다.
DB, Sleep이 있을 때 결과
Jmeter 설정
Number of Threads : 250
Ramp-up period : 1
Loop Count : 1
1. Normal Thread
2. Virtual Thread
SpringBoot에서 blocked를 유도하는 메서드를 사용했을 때는 Virtual Thread가 훨씬 우세했었습니다.
하지만, 현재 DB IO Test에서 Virtual Thread를 사용한 결과 Virtual Thread의 처리량이 훨씬 적습니다.
특히 Normal Thread의 처리량이 초당 9회 언저리에 머물고 있는 반면, Virtual Thread의 처리량은 초당 4회 이하인 것을 볼 수 있습니다.
중간 점검
위의 결과에서 추측할 수 있는 부분은 아래와 같았습니다.
1. Normal Thread, Virtual Thread 모두 '어떠한 제약 조건'에 의해 상한선이 막혀있다.
2. Normal Thread, Virtual Thread 의 '어떠한 제약 조건'은 서로 다르다.
이 문제를 해결하기 위해서, '어떠한 제약 조건'을 알아낼 필요성을 느꼈고, 해당 제약 조건을 찾기 시작했습니다.
지금은 스프링부트 웹개발을 배우게 된 지 얼마 되지 않았습니다.
하지만, 앞으로 외부 IO작업을 맡을 때 이렇게 외부 서버의 처리량이 제한된 경우 대응방법이 필요할 것이라 생각했습니다.
특히 위의 궁금했던 상황처럼, 수많은 데이터를 다룰 때 유동적으로 시간이 걸리는 경우 어떤 부분을 신경써서 처리량을 최적화하고 유저에게 응답을 주어야 할 지 고민했습니다.
--> 다음 포스팅에 계속
'WEB > Spring' 카테고리의 다른 글
[Spring] CaffeineCache 사용해보기, 성능테스트 (0) | 2024.04.07 |
---|---|
[Spring] HikariCP 알아보기 (0) | 2024.01.14 |
[Spring] Actuator (1) | 2024.01.04 |
[Spring] SpringBoot에서의 Virtual Thread (2) | 2023.12.31 |
[Spring] Spring의 ThreadPool (1) | 2023.12.31 |