ngrinder 를 통한 좋아요 관련 기능 성능 테스트(조인vs조인x) + (비관적락vs레디스)

은찬·2023년 12월 4일
2

ngrinder 를 통한 좋아요 관련 기능 성능 테스트

지난 포스팅에서 아래 두 가지 경우의 성능 차이에 대해서 알아봤다. 이번 포스팅에서는 직접 성능 테스트를 하며 성능 차이에 대해서 자세히 분석해보겠다

  1. 좋아요에 대한 조인을 포함한 리뷰 조회 쿼리 vs 순수 리뷰 조회 쿼리
  2. 비관적 락을 적용한 좋아요 기능 vs 레디스를 적용해 락이 없는 좋아요 기능

테스트 환경 및 비교군

서버

AWS EC2 프리티어

DB

AWS RDS 프리티어

사용 툴

ngrinder 툴을 사용해서 테스트를 진행했다

환경

단순 성능만 비교하는게 목적이기 때문에 사용자는 100명정도로 설정했고 테스트 시간은 짧게 1분을 가져갔다.
그리고 Ramp-up 기능은 사용하지 않았다.

비교군

초당 트랜잭션 처리 수를 나타내는 TPS 를 비교군으로 설정했다

참고 사항

  • 각 방법에 대해서 성능의 차이를 비교하는 테스트기 때문에 각각의 수치보다는 각 테스트의 성능 지표의 차이에 집중하면 좋을 것 같다 🫡
  • 성능의 차이를 비교하기 위한 테스트기 때문에 환경 구성을 짧은 시간동안에 적당한 사용자수로 지정했다

쿼리 성능 비교

조건

1번 조건 : 좋아요에 대한 조인을 포함한 쿼리(자세한 쿼리가 궁금하다면 이전 포스팅을 참고!) + 토탈 좋아요 개수 3만개

@Transactional(readOnly = true)
@GetMapping("/join")
public void withJoin() {
	reviewQueryDslRepository.joinWithLikeQuery();
}

2번 조건 : 좋아요에 대한 조인이 없는 쿼리 + Redis 에서 좋아요 개수를 조회

@Transactional(readOnly = true)
@GetMapping("/no_join")
public void withoutJoin() {
	List<Review> reviews = reviewQueryDslRepository.noJoinQuery();
	List<Long> reviewIds = reviews.stream().map(Review::getId).toList();
	likeReviewService.getLikeCounts(reviewIds);
}

결과

1번 좋아요에 대한 조인을 포함한 쿼리

2번 좋아요에 대한 조인이 없는 쿼리 + Redis 에서 좋아요 개수를 조회

1번의 경우는 TPS 가 7이 나왔고, 2번의 경우는 TPS 가 159 가 나오며 정말 큰 성능 차이를 보여줬다 🔍
현재 조회되는 리뷰에 대한 총 좋아요가 3만개가 있어서 차이가 큰데, 좋아요 개수는 항상 많아질 수 있고, 그 성능 차이는 더욱 커질 것이다.
특히 현재 좋아요 개수로 정렬을 제공하는데 이런 경우엔 성능이 더 최악일 것이다. 때문에 1번 방법은 옳지 않다고 생각한다


좋아요 기능 성능 비교

조건

1번 조건 : 비관적 락을 적용한 좋아요 로직이며 흐름은 아래와 같다

  1. 좋아요 entity 를 저장한다
  2. lock 을 취득하며 리뷰를 조회
  3. 리뷰의 좋아요 개수 갱신
@Transactional
public void withLock(Long reviewId, Long userId) {
	likeReviewRepository.save(LikeReview.addLike(userId, reviewId));

	Review review = reviewFindService.getWithLockById(reviewId);
	review.addLike();
}

2번 조건 : 락 없이 레디스를 적용한 좋아요 로직이며 흐름은 아래와 같다

  1. 좋아요 entity 를 저장한다
  2. redis 에서 해당 리뷰에 대한 좋아요 개수를 조회
  3. 데이터가 있다면 INCR 를 통해 좋아요 개수 갱신
  4. 데이터가 없다면 count 쿼리를 통해 새로운 좋아요 개수 갱신
@Transactional
public void withoutLock(Long reviewId, Long userId) {
	likeReviewRepository.save(LikeReview.addLike(userId, reviewId));

	redisUtils.getData(reviewId)
		.ifPresentOrElse(
			likeCount -> redisUtils.increaseData(reviewId),
			() -> {
				Long likeCount = likeReviewRepository.countByReviewId(reviewId);
				redisUtils.setData(reviewId, likeCount);
			}
		);
}

결과

1번 비관적 락을 적용한 좋아요

2번 락 없이 레디스를 적용한 좋아요

1번의 경우는 TPS 가 70 수치가 나왔고, 2번의 경우는 TPS 가 177 가 나오며 큰 성능 차이를 보여줬다.
1번 방식에서는 락을 취득하고 추가적으로 쿼리도 두번 실행되며 락을 쥐고있는 시간이 꽤 길다는 것을 볼 수 있었다
현재 프리티어라 DB 자체의 스케일업을 하면 락을 쥐는 시간이 꽤 줄어들며 차이가 어느정도 줄어들 것 같지만 감안해도 그래도 큰 차이다

전체적 결론

좋아요에 대해서 좋아요 자체 기능과 관련 조회 기능에 대해서 여러가지 구현 방법에 대해 성능 테스트를 진행해봤다.

확실히 수치로 증명되니까 문제점에 대해서 명확하게 인지하게 되었다. 그리고 이러한 수치는 내 코드의 신뢰성을 뒷받침해주었다.

이번 포스팅에서는 성능 비교에만 초점을 맞추며, 테스트 환경을 비교적 간단히 구성했지만 다음에는 서버의 가용성에 초점을 맞춘 여러 성능 테스트를 진행해볼 예정이다 🫡

부록

필자는 처음에 Nginder 를 사용할 때 도커를 이용한 방법을 시도했는데, 스크립트 검증에서 자꾸 실패해서 war 파일을 통한 방법으로 시도해보니까 성공적으로 스크립트 검증이 진행됐다.

참고 자료

https://www.youtube.com/watch?v=3cTn53dtzJI&ab_channel=우아한테크

https://liltdevs.tistory.com/169

https://choibulldog.tistory.com/61

https://www.whatap.io/ko/blog/14/

profile
`강한` 백엔드 개발자라고 해두겠습니다

0개의 댓글