지난 포스팅에서 아래 두 가지 경우의 성능 차이에 대해서 알아봤다. 이번 포스팅에서는 직접 성능 테스트를 하며 성능 차이에 대해서 자세히 분석해보겠다
AWS EC2 프리티어
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번 조건 : 비관적 락을 적용한 좋아요 로직이며 흐름은 아래와 같다
@Transactional
public void withLock(Long reviewId, Long userId) {
likeReviewRepository.save(LikeReview.addLike(userId, reviewId));
Review review = reviewFindService.getWithLockById(reviewId);
review.addLike();
}
2번 조건 : 락 없이 레디스를 적용한 좋아요 로직이며 흐름은 아래와 같다
@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