[Spring] 쿼리 1번이 TPS가 더 잘나올까? 쿼리 2번이 TPS가 더 잘나올까?

private void rewardUserItem() {
  final int userId = 1;
  List<UserItem> userItems = getUserItems(userId);
  checkUserItemInventoryCount(userItems);
  checkAlreadyExistsUserItem(userItems);
}

private List<UserItem> getUserItems(int userId) {
  return userItemRepository.findByUserId(userId);
}

private void checkUserItemInventoryCount(List<UserItem> userItems) {
  int userItemCount = userItems.size();
  final int maxItemInventory = 100;
  
  if (userItemCount > maxItemInventory) {
    throw new IllegalStatementException("아이템 저장공간이 부족합니다.");
  }
}

private void checkAlreadyExistsUserItem(int itemId, List<UserItem> userItems) {
  boolean existsUserItem = userItems
      .stream()
      .anyMatch(ui -> ui.getItemId().equals(itemId));
  
  if (existsUserItem) {
    throw new IllegalStatementException("아이템을 이미 보유중입니다.");
  }
}

@Entity
public class UserItem {
  @Id
  @Column
  private userId;
  
  @Column
  private itemId;
}

회사에서 위의 코드를 리뷰하는 중 리뷰어로부터 의견을 받음

아래는 대화 내용

 

리뷰어: findByUserId 메서드를 사용하면 ROW가 여러개 나올 수 있다.

리뷰어: checkUserItemInventoryCount 메서드에서는 countByUserId, checkAlreadyExistsUserItem 메서드에서는 existsByItemId 메서드를 사용하는게 낫지 않냐

글쓴이: 쿼리를 2번 호출하는 것보다, 한번 호출해서 웹 어플리케이션단에서 처리하는게 성능이 더 잘 나올 것 같다.

글쓴이: 두 방법을 비교해 봤을 때 성능 차이가 크게 나지는 않을 것 같다. 그래서 내 생각에는 리뷰어님이 제안한 코드도 product 환경에 적용해도 문제 없고, 내가 올린 코드도 product 환경에 적용해도 문제 없다고 생각한다.

리뷰어: 정확히는 성능을 비교 해봐야겠지만, 일단 알겠다.

요런 대화를 나눈 후 내 코드가 repo에 올라갔다.

대화를 하고 난 후 궁금해서 리뷰어가 말씀하신 방법으로 API를 한개 더 만들어서 nGrinder로 2개의 API를 각각 TPS 측정해봤다.

측정하고 보니 리뷰어가 말한 방법의 API가 TPS가 높았다.

SSMS로 3개의 쿼리를 실행 계획 돌려보니 모두 인덱스 SEEK를 타고 있었다. 비용 면에서도 모두 동일한 비용을 사용하고 있었다.

hibernate 통계 분석 설정(generate_statistics)을 추가한 후 확인해보니, getUserItems 메서드로 받는 List가 53개 넘어가면서부터 쿼리 실행 속도가 매우 많이 올라가는 현상이 있었다.

추측하건데 실행 계획과 실제로 쿼리 실행 후 완료까지 걸리는 시간은 별개인 것 같다.

상황에 따라 쿼리를 한번만 사용하는 경우가 성능이 잘 나올 수 있지만, 이런 경우는 리뷰어 말씀대로 쿼리 2번을 사용하는게 좋을 것 같다.