들어가기 앞서
Volunteer 사이드 프로젝트를 진행하면서 Spring Data JPA를 이용해 exists 함수를 사용해야할 상황이 있었다.
Spring Data JPA는기본 메서드로 existsBy~ 형태로 제공하지만,
where절의 조건이 3개,4개... 증가할 수록 필드명이 너무 길어져 @Query를 사용합니다.
대부분 아시다시피, Spring Data JPA는 exists 함수를 제공하지 않기 때문에, 이를 대체하고자 Querydsl를 사용합니다.
또한, Querydsl은 서브쿼리로만 exists 함수를 제공하고 성능상 이슈가 있어 직접 구현해야하는 걸로 알고 있습니다.
직접 얼만큼 성능차이가 있는지 눈으로 보기 위해서 몇가지 경우를 직접 성능 테스트 해보았습니다.
- Querydsl의 기본 exists 함수
- Spring Data JPA의 기본 JpaRepository exists 함수
- Querydsl의 selectOne + fetchFirst (직접 구현)
참고로, 150만개 더미 데이터를 통해 성능 테스트를 진행해보았습니다.
Querydsl 기본 exists 성능 테스트
앞서 말했듯이, Querydsl의 exists는 서브쿼리 형태로만 지원되기 때문에 어쩔수 없이 where문에 사용했습니다.
또한, Querydsl는 from절 없이는 생성이 안되기 때문에, select 문에 exists를 사용할 수 없습니다.
성능을 살펴보면, 약 2.6초가 걸리는 것을 볼 수 있다.
해당 글을 작성하면서 참고한 향로님의 블로그에서, Querydsl의 exists는 내부적으로 fetchCount()>0을 통한 count 방식을 사용해 성능이 떨어진다고 지적했습니다.
하지만, 현재는 이 방식이 Deprecated 되었고, limit 방식을 사용하는 fetchFirst() 방식으로 바뀌었습니다.!!
하지만, 여전히 서브쿼리 방식으로만 exists를 사용할 수 있고, where 절에 사용해야하기 때문에 성능이 좋지 않습니다.
현재 테스트에서는 150만개의 데이터만 사용했지만, 데이터가 증가할수록 성능은 계속 떨어질 것입니다.
Spring Data JPA의 JpaRepository exists 성능 테스트
결과를 보면, 약 0.9 초가 소요되고 앞 방식 보다 약 2배 이상 줄어든 것을 볼 수 있습니다.
Spring Data JPA가 제공해주는 기본 exists 메소드는 내부적으로 limit 방식을 사용하고 있어, 첫번째 결과가 나오면 바로 true를 리턴해줍니다. 그러므로 count 같이 총 데이터를 조회할 필요 없으므로 성능이 뛰어납니다.
만약 더미 데이터가 더 증가하게 되더라도, Where 절에 사용될 조건문이 그대로 유지될 경우 항상 비슷한 성능이 보장될 것입니다.(다음 케이스에서도 동일)
Querydsl의 selectOne+fetchFirst 성능 테스트
결과를 보게되면, Spring Data JPA 방식과 비슷한 성능인 약 0.7초가 소요되는 것을 볼 수 있습니다.
Querydsl의 fetchFirst()는 내부적으로 limit 방식을 사용하고 있어, 앞 방식와 유사한 성능이 나오게 됩니다.
마치며
Querydsl의 exists 를 직접 구현해서 사용하면서, 실제 얼마나 성능에서 차이나는 지 궁금했습니다.
이번 기회에 여러 케이스에서 테스트 해보며 성능 차이를 실제 눈으로 볼 수 있어서 명확히 이해가 되었습니다.
참고 자료
'JAVA > JPA & QueryDSL 학습 기록' 카테고리의 다른 글
JPA saveAll()의 N+1 문제 (1) | 2024.02.01 |
---|