[스프링] [JPA] 페이징 성능 개선: 커버링 인덱스

Covering Index


페이징 성능을 개선할 방법으로는 NoOffset 페이징, 커버링 인덱스를 사용하는 것이 있다.

NoOffset페이징은 페이지 번호를 의미하는 offset을 쓰지않고, 그대신 이전 페이지의 끝부분을 가지고있다가 조회의 첫 부분을 찾을 때 사용하는 것이다. 이 방법은 특정 페이지별로 조회가 안된다는 단점이 있다.

현재 진행중인 프로젝트에서는 해당 방법은 사용할 수 없어서 커버링 인덱스를 사용해 개선했다.

커버링 인덱스란 쿼리를 충족시키는 데 필요한 데이터를 갖고 있는 인덱스를 말한다.

커버링 인덱스를 이용해 출력할 데이터들의 id값을 조회한 후, 해당 id값이 담긴 List를 이용해 데이터를 조회하는 방식으로 구현했다.

이렇게 하면 커버링 인덱스를 구할 때 where, order by, offset, limit를 다 처리하여 id값만 빠르게 가져오고, 걸러진 데이터들에 대해서만 접근하기 때문에 성능 개선을 할 수 있다.

Querydsl을 이용해 구현했고, 코드의 예시는 다음과 같다.

private List<RankingResponseDto> getRankingCoveringIndex(String country, String author, Pageable pageable) {
    JPAQueryFactory query = querydslConfig.jpaQueryFactory();

    // 커버링 인덱스로 대상의 id값만 가져온다.
    List<String> idList = query
        .select(book.id)
        .from(book)
        .where(
            countryEq(country),
            authorEq(author)
        )
        .orderBy(book.rate.desc())
        .offset(pageable.getOffset())
        .limit(pageable.getPageSize())
        .fetch();

    // 걸러진 id값을 이용해 데이터에 접근하여 반환한다.
    return query
        .select(Projections.constructor(RankingResponseDto.class,
            book.name,
            book.rate,
            book.author,
            book.country
        ))
        .from(book)
        .where(book.id.in(idList))
        .orderBy(book.rate.desc())
        .fetch();
}
  • 커버링 인덱스를 적용하기 전

  • 커버링 인덱스를 적용한 후

커버링 인덱스를 적용하기 전과 비교해보니 3배 가까이 속도 개선이 된 것을 확인할 수 있었다.