JPA exists 쿼리 성능 개선

dragonappear·2022년 7월 12일
0

Spring Data

목록 보기
1/1

출처

제목: "JPA exists 쿼리 성능 개선"
작성자: tistory.com(jojoldu)
작성자 수정일: 2020. 8. 6
링크: https://jojoldu.tistory.com/516
작성일: 2022년7월12일

Spring Data Jpa를 사용하다보면 해당 조건의 데이터가 존재하는지 확인 하기 위해 exists 쿼리가 필요할 때가 있다.

간단한 쿼리의 경우엔 아래와 같이 메서드로 쿼리를 만들어서 사용한다.

boolean existsByName(String name);

복잡해지면 메서드명으로만 쿼리를 표현하기가 어려워진다

조건문이 3개 이상 이거나, 필드명이 너무 길거나 조건문 자체가 복잡한 등등

그래서 이런 경우엔 보통 Spring Data Jpa의 JPQL @Query를 사용한다

다만 이 경우에 JPQL에서 select의 exists를 지원하지 않는다 ( select exists)

단 Where의 exists는 지원한다.

exists를 사용할 수 없기 때문에 아래와 같이 count 쿼리를 사용할 수 있다.

@Query("SELECT COUNT(o.id) > 0 " +
        "FROM Order o " +
        "WHERE o.txDate =:txDate")
    boolean exists(@Param("txDate") LocalDate txDate);

하지만 이 방식은 성능상 이슈가 있다.

어떤 이슈가 있는지 확인해보자

Count vs Exists

SQL로 직접 쿼리를 날려서 성능을 비교해보면 차이를 느낄 수 있따.

5600만건 정도 쌓여있는 테이블을 기준으로 countexsists를 수행해보면 다음의 성능 차이를 확인할 수 있다.

거의 2배에 가까운 성능 차이를 확인할 수 있다.

데이터가 더 늘면 차이는 더 벌어질것이다.

왜 이렇게 성능 차이가 날까?

이는 exists는 첫번째 결과에서 바로 true 로 리턴하지만, count의 경우에는 N(전체 데이터)를 확인해봐야 하기 때문에 당연하게 성능 차이가 발생한다.

위 결과로 count를 쓰면 억울할 것이다.


1. QueryDSL에서 exists

QueryDSL exists를 보면 fetchCount를 사용하지 않고 fetchFirst()를 사용함을 알 수 있다.

fetchFirst() : limit(1).fetchOne() 이다.

public Boolean exist(Long bookId) {
	Boolean	result = queryFactory
            .selectOne()
            .from(book)
            .where(book.id.eq(bookId))
            .exists();

	return result;
}

즉, exists 가 필요한 경우엔 JQPL의 count보다 QueryDSLexists 를 사용하자


2. limit 1 사용

public Boolean exist(Long bookId) {
    Integer fetchOne = queryFactory
            .selectOne()
            .from(book)
            .where(book.id.eq(bookId))
            .fetchFirst(); // limit 1

    return fetchOne != null; // 1개가 있는지 없는지 판단 (없으면 null이라 null체크)
}

fetchFirst() 는 내부적으로 limit(1).fetchOne() 로 되어있다.

즉, exists 가 필요한 경우엔 limit 1로 대체가능하다.


3. 메서드 쿼리

메서드 쿼리의 exists는 아래와 같이 limit으로 쿼리 최적화를 내부적으로 하고 있다.

간단한 조건의 exists가 필요할 때는 메서드 쿼리로 구현해도 될 듯하다.


정리

  • countexists에 비해 성능상 안좋다

    • @Query와 QueryDSL에서는 select exists를 사용할 수가 없다.
  • 그래서 select existslimit 1로 대체해서 사용한다.

  • 단, JpaRepository의 메소드 쿼리에선 내부적으로 limit 1를 사용하고 있어서 성능상 이슈가 없다.

0개의 댓글