상품 데이터 10만 개를 넣어서 검색 성능 테스트를 했을 때 성능 이슈를 발견
상품 이름 중에 케이크가 들어간 상품들을 검색하기 위해서 Like '%단어%'와 같이 양쪽에 와일드카드를 붙였을 때 인덱스를 사용하지 않고, Full Scan을 하기 때문에 검색 성능이 느려진다.
Offset 기반의 페이지네이션은 Spring JPA에서 Page, Pageable 객체를 사용하면 쉽게 구현할 수 있다. 하지만 이러한 편리성이 있는 대신 단점이 존재한다.
Offset 방식은 항상 첫페이지부터 Offset에 해당하는 페이지로 탐색을 하기 때문에 페이지가 뒤로 갈수록 성능이 저하된다는 것이다.
이렇게 500페이지에 대한 Like Query 조회를 했을 때 14초의 성능 이슈를 발견
정규 표현식, Full Text 검색, Elastic Search와 같은 전문 검색 엔진 사용하기와 같은 방법들이 있었지만 정규표현식과 Full Text 검색으로는 성능 이슈가 해결되지 않았고, Elastic Search를 뒤늦게 도입하기에는 시간이 오래 걸린다는 단점이 있기에 Redis Caching을 적용하기로 했다.
디스크 기반인 MySQL보다 메모리 기반인 Redis이 더 빠른 응답시간을 제공함 -> 결론적으로, 성능 향상 및 서버의 부하를 줄임
Redis Caching 데이터의 만료기한을 1분으로 짧게 준다.
1분만 해도 많은 유저가 사용하는 환경에서 충분히 서버의 부하를 줄여줄 수 있다고 생각했다.
Offset 방식의 페이지네이션은 MySQL의 Offset과 Limit를 사용하는 것이다.
SELECT *
FROM table
ORDER BY id
OFFSET 15
LIMIT 5
이것을 No Offset 즉, 커서 방식의 페이지네이션을 하기 위해서 인덱스를 지정해서 Offset 방식의 단점을 해결한다.
SELECT *
FROM table
WHERE id > product_id
ORDER BY id
LIMIT 5
id로 정렬을 했을 때, 현재 페이지의 마지막 상품 아이디보다 큰 아이디를 조건으로 걸면 처음부터 읽어야 한다는 Offset 방식의 단점을 해결할 수 있었다.
@Query(value = "select p.id, p.name, p.price, p.thumbnail " +
"from product p " +
"where p.name like %:keyword% " +
"and p.id > :productId " +
"limit 10"
, nativeQuery = true)
List<IProduct> searchKeyword2(@Param("keyword") String keyword, @Param("productId") Long productId);
결론적으로, JpaRepository에서 위와 같은 코드를 작성했다.
14초 가량에서 최종적으로 0.02 퍼포먼스가 향상된 것을 확인할 수 있다.