페이징 성능 개선을 위해 No Offset 사용하기
#java
기존 페이징 방식인 offset 과 limit 를 이용한 방식은 서비스가 커짐에 따라서 장애를 유발할 수도 있다.
이유로는 초기엔 데이터가 적어서 문제가 없지만 데이터가 점점 많아지면 느려지기 때문인데 결국에는 offset 을 이용하면 offset + limit 만큼의 데이터를 읽어야 하기 때문이다.
일단 offset 을 이용하는 기존 페이징 쿼리는 다음과 같다.
SELECT *
FROM items
WHERE 조건문
ORDER BY id desc
OFFSET 페이지 번호
LIMIT 페이지 사이즈
이와 같은 형태는 페이지 번호가 뒤로 갈수록 앞에서 읽었던 행을 다시 읽어야 한다.
이 말은 offset이 10000이고 limit가 20이라면 10,020 행을 읽어야 한다는 것이고 그러고 나서 10,000 개의 행을 버리는 것이다.
그렇기 때문에 성능 상에 안좋다는 점인데 No Offset 방식은 시작 지점을 인덱스로 빠르게 찾아 첫 페이지부터 읽도록 하는 방식이다.
No Offset 을 이용하는 SQL 문은 다음과 같다.
SELECT *
FROM items
WHERE 조건문
AND id < 마지막 조회 ID
ORDER BY id desc
LIMIT 페이지 사이즈
- 이전에 조회된 결과를 한번에 건너뛸 수 있게 마지막 조회 결과의 ID 를 조건문에 사용하는 방식을 이용한다.
NoOffset 예제 코드
public List<MemberDto> noOffset(Long lastMemberId, int limit){
return queryFactory
.select(new QMemberDto(
member.username,
member.age
))
.from(member)
.where(member.username.contains("member")
.and(memberIdLt(lastMemberId)))
.orderBy(member.id.desc())
.limit(limit)
.fetch();
}
private BooleanExpression memberIdLt(Long lastMemberId) {
return lastMemberId != null ? member.id.lt(lastMemberId): null;
}
문제점
일반적으로 많이 사용하는 페이지 번호 선택식 페이징의 경우에는 이 방식을 사용할 수 없다.
페이지를 선택해서 이동할 경우 그 전 페이지의 마지막 Element를 알 수 없기 때문이다.
보통 No Offset을 통해 성능 향상을 기대해 볼 수 있는 방식은 무한 스크롤 방식의 페이징이다.