대충 넘어가지 않는 습관을 위한 기록

no-offset 적용 기록

uhyvn 2024. 8. 19. 00:33

현재 서비스에서는 게시글 조회 시 최신순 정렬이 기본값이다.

 

원래 기존에 작성해주신 코드는, 쉬운 이해를 위해 아래 기본적인 코드로 예시를 작성하겠다.

    @Override
    public List<Post> getPosts(int pageNum, int pageSize) {
        return queryFactory.selectFrom(post)
                .orderBy(post.createdAt.desc()) // 최신순으로 정렬
                .offset((pageNum - 1) * pageSize) // 페이지 번호를 기준으로 OFFSET 계산
                .limit(pageSize) // 한 페이지에 표시할 결과 수
                .fetch();
    }

jpa 쿼리에서도 offset 쿼리가 포함되어 나가고,

querydsl도 보통 이런 식이다. (slice 처리 코드 등 세부 코드는 포함하지 않았다.)

 

 

그런데 이렇게 조회를 하게 되면, 전에 읽었던 행들을 다시 또 읽어야 하기 때문에 offset이 증가할 수록 탐색 속도가 느려진다는 문제가 생긴다.

 

이제는 많이 유명해진 no-offset 방식인데, 생각보다 적용하지 않은 코드들이 종종 보인다.

 

아래는 no-offset 방식을 적용한 쿼리다.

    @Override
    public List<Post> getPosts(Long lastPostId, int pageSize) {
        return queryFactory.selectFrom(post)
        	.where(Objects.isEmpty(lastPostId) ? null : dailyLife.id.lt(lastPostId))
                .orderBy(post.createdAt.desc()) // 최신순으로 정렬
                .limit(pageSize) // 한 페이지에 표시할 결과 수
                .fetch();
    }

여기서 null 체크를 해주는 이유는,

만약 맨 첫 페이지를 조회할 때, 클라이언트 입장에서는 현재 db에서 제일 최신 게시글 id를 알 수가 없기 때문이다.

그래서 id가 null이라면, where절이 없이 최신순으로 첫 페이지를 조회하는 것이고,

id값이 존재한다면 where절을 추가해서 이전 게시글 id보다 작은 id를 가진 게시글만 조회하게끔 한다.

 

 

 

현재 서비스에서 테스트한 결과, 위 두 코드의 성능 차이는 이러하다.

 

위 사진이 offset 적용 시, 그리고 아래 사진이 no-offset 방식을 적용했을 때이다.

 

물론 데이터 양도 많지 않고, where절이나 다른 코드에 의해 성능 차이가 날 수 있지만,

확연히 빠른 성능을 보이는 것은 사실이다..!