NDM

Jmeter를 이용한 이벤트 상품 조회 api 부하 테스트와 개선일지 본문

[프로젝트] 타임딜 API 서버

Jmeter를 이용한 이벤트 상품 조회 api 부하 테스트와 개선일지

ndm.jr 2023. 6. 1. 21:29

시간 안에 세일한 상품 이벤트 프로모션을 진행하는 커머스 플랫폼 api 서버 프로젝트를 진행중입니다.

 

Jmeter를 이용해 테스트할 api는 크게 주문, 상품조회로 2가지 입니다. 이번 포스팅에서는 조회 api만 다루겠습니다.

 

Jmeter는 로컬에서 돌리고, Spring서버는 로컬에서 한번, 운영에서 한번 해보는 것으로 진행하였습니다.

 

Jmeter와 SpringBoot를 같은 서버에서 진행하면 제대로 된 테스트를 할 수 없기에,

 

로컬에서 함께 진행할 때는 적은 요청으로 무리가 없는지, Jmeter 설정이 제대로 이루어졌는지만 보는식으로 진행하고

운영에서 따로 진행하였습니다.

 

* 기본적으로 Jmeter를 돌리는 제 노트북 성능이 별로 좋지 않아 제대로 된 성능이 나오지 않습니다.

 


# 상품조회

 

상품 조회는 다음과 같이 진행됩니다.

 

Spring 서버는 GCP의 VM인스턴스를 사용했으며, e2-medium 모델입니다.(vCPU 2개, 4GB 메모리)

 

테스트 데이터로 상품 100만개가 존재하고, 그 중 1만개만 이벤트 상품입니다.

기본적으로 이벤트 페이지에 처음 들어가면 만나게 되는 1 page를 GET방식으로 요청했고, 1page에 상품은 10개가 default입니다.

 

이벤트 상품 조회 api를 1초에 10000번 요청한 결과입니다.

 

 

 

Jmeter의 Response Time Graph로는 첫 요청때 1,800ms까지 올라갔다가 그 이후 100ms대로 내려왔습니다.

첫 요청때는 캐싱되어있지 않아 RDB를 찌른것이고, 그 이후 Redis를 조회했기에 이런 결과가 나온것으로 보입니다.

 

 

 

에러는 없고, 평균적으로 500ms안쪽으로 시간이 소요된 것으로 보입니다.

Throughput은 1초당 400개 정도의 요청을 처리했다고 나오는데.. 만약 테스트용 서버가 별도로 있었다면 더 좋지 않았을까 예측해 봅니다.

Pinpoint
Grafana : Redis exporter

 

MySQL로 간 Request도 별로 없고, Redis도 요청을 보낸 시간대에 확 튄걸 봐서는 캐싱이 잘 된것 같네요.

 

그러나 Spring, Redis, MySQL 모두 CPU나 메모리 사용량이 아직까지 괜찮았던걸 봐서는 Connection을 조금 늘려주면 성능 개선을 조금 더 해볼수 있지 않을까 싶습니다. 게다가 mysql bash에 접속하려고 하니 too many connections..가 떴기 때문에, mysql설정을 조금 바꿔보도록 하겠습니다.

 

wait_timeout을 기본값이었던 28800에서 300초로, max_connections를 151개에서 500개 정도로 변경했습니다.

 

RDB에 접근해 데이터를 가져오는 시간이 확 줄었습니다. Connection개수도 어느정도 영향이 있는 것 같습니다.

 

성능이 개선된것을 확인할 수 있습니다. 이번에는 초당 700개 정도의 요청을 처리한다고 나와있습니다.

제 로컬 서버 말고 제대로 테스트해보고 싶긴 합니다.

 

 

 

성능을 제대로 측정해보기 위해 혹시나 해서 MySQL쿼리 캐시도 확인해보았는데 쿼리 캐시의 영향은 없는 것으로 나타났습니다.

 

 


# 그 밖에?

 

# 간단한 쿼리튜닝

 

제 상품쪽 ERD는 다음과 같이 간단하게 되어있습니다.

 

원래 이벤트 상품을 조회할 떄, Product부터 ProductEvent, PublishEvent까지 같이 패치 조인해

데이터를 한번에 가져오는 방식으로 조회를 했었습니다. 1:1, N:1이라 패치조인에 문제가 없었기 떄문입니다.

 

 

다만 이런식으로 하면, where절에서 publishEvent의 변수만 사용하는데 비해 카운트쿼리에서도 2개 테이블을 조인해야 합니다.

때문에 Product부터 PublishEvent까지 패치조인으로 묶어서 조회하기보다, ProductEvent를 중심으로 조인을 시작했습니다. 단, ProductEvent는 사실 Product와 PublishEvent의 매핑테이블 역할이라 생각해 따로 Repository를 생성하지는 않았고, JPQL로 해결하였습니다.

 

 

이렇게 하면 count쿼리에서는 Product라는 조인테이블을 하나 줄일 수 있었습니다.

상품이 초기데이터로 100만개를 집어넣어놨었기에, 튜닝 전 쿼리로 테스트를 시도했다면 더 좋지 않은 결과가 나왔을 것으로 보입니다.