페이징 + Querydsl
1. Entity를 직접 조회
public Page<Post> getPostsList(Pageable pageable, String keyword) {
BooleanBuilder builder = new BooleanBuilder();
if(StringUtils.hasText(keyword)) {
builder.and(post.title.like("%" + keyword + "%"));
}
List<Post> postsList = query
.selectFrom(post)
.where(builder)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(post.id.desc())
.fetch();
//count 구하기
JPAQuery<Long> countQuery = query
.select(post.count())
.where(builder)
.from(post);
return PageableExecutionUtils.getPage(postsList, pageable, countQuery::fetchOne);
}
2. 커버링 인덱스 사용
public Page<Post> getPostsCoveringIndexList(Pageable pageable, String keyword) {
BooleanBuilder builder = getBooleanBuilder(keyword);
List<Long> ids = query
.select(post.id)
.from(post)
.where(builder)
.orderBy(post.id.desc())
.offset(offset)
.limit(size)
.fetch();
List<Post> postsList = query
.selectFrom(post)
.where(post.id.in(ids))
.orderBy(post.id.desc())
.fetch();
//count 구하기
JPAQuery<Long> countQuery = query
.select(post.count())
.from(post)
.where(builder);
return PageableExecutionUtils.getPage(postsList, pageable, countQuery::fetchOne);
}
3. DTO 이용
public Page<PostListResponseDto> getPostsDtoList(Pageable pageable, String keyword) {
BooleanBuilder builder = getBooleanBuilder(keyword);
List<Long> ids = toPostIds(pageable.getOffset(), pageable.getPageSize(), builder);
List<PostListResponseDto> postsList = query
.select(new QPostListResponseDto(
post.id,
post.title,
user.name,
post.createdDate)
)
.from(post)
.leftJoin(post.user, user)
.where(post.id.in(ids))
.orderBy(post.id.desc())
.fetch();
//count 구하기
JPAQuery<Long> countQuery = query
.select(post.count())
.from(post)
.where(builder);
return PageableExecutionUtils.getPage(postsList, pageable, countQuery::fetchOne);
}
정리
- 처음 커버링 인덱스를 적용했을 때 성능에 큰 차이가 있었다.
- DTO로 변환했을 때에는 미묘한 차이만 있었다. 그래도 보내는 쿼리 수가 줄어든 것에 만족
- 1억건으로 데이터를 늘렸을 때 최적화에 한계를 느꼈다.
- 초반 페이지는 속도가 빠른데 1억 건 이상 데이터가 들어가니 count query 수행 시간이 오래 걸렸다.
- 페이지가 뒤로 갈수록 offset으로 인한 속도 저하가 있었다.
번외) No Offset
public Slice<PostListResponseDto> paginationNoOffset(
Long postId, Long userId, Pageable pageable, String keyword) {
BooleanBuilder builder = new BooleanBuilder();
if(postId != null) {
builder.and(post.id.lt(postId));
}
builder.and(post.user.id.eq(userId)).and(getBooleanBuilder(keyword));
List<PostListResponseDto> posts = query
.select(new QPostListResponseDto(
post.id,
post.title,
user.name,
post.createdDate)
)
.from(post)
.leftJoin(post.user, user)
.where(builder)
.limit(pageable.getPageSize()+1)
.orderBy(post.id.desc())
.fetch();
return getSliceImpl(pageable, posts);
}
// 다음 페이지가 있는지 확인
private static SliceImpl<PostListResponseDto> getSliceImpl(Pageable pageable,
List<PostListResponseDto> posts) {
if(posts.size() == (pageable.getPageSize() + 1)) {
return new SliceImpl<>(posts.subList(0, pageable.getPageSize()), pageable, true);
}
return new SliceImpl<>(posts, pageable, false);
}
- no offset 방식은 확실히 빨랐다.
- 더보기 버튼만 이용하여 다음 페이지를 중첩하여 불러왔다.
- 그러나 게시판이라면 개인적으로 페이지 이동이 꼭 필요하다고 생각했다.
'개발 > Spring' 카테고리의 다른 글
| [JPA] API 조회 기능 개발 정리 (0) | 2023.03.28 |
|---|---|
| [JPA] 연관관계 설정 예시 (0) | 2023.03.28 |
| [SpringBoot] MariaDB 연동 / querydsl 설정 (0) | 2022.10.12 |
| [SpringBoot] 데이터 조회 (0) | 2022.09.14 |
| [SpringBoot] Mustache를 이용하여 화면 만들기 (0) | 2022.09.08 |