Database/Oracle

[Oracle] 페이지 처리(Offset-based, Cursor-based)

JiWonSon 2021. 1. 6. 18:59

페이지네이션(Pagination)?

 

 서버에서 데이터를 가져올 때 모든 데이터를 한번에 가져올 수 없다.

따라서 특정한 정렬 기준에 따라 지정된 갯수의 데이터를 가져오는 것이 필요한데 이를 "페이지네이션"이라 한다.

 

예를들어 만개의 데이터가 저장되어 있을 때 한번에 만개를 전달하는 대신 0번~50번을 먼저 전달 후 다음 요청에서 50번 부터 100번까지 제공하는 것이다. 이렇게 하면 네트워크의 낭비를 막고 빠른 응답을 기대할 수 있기 때문에 널리 사용해왔다.

 

페이지네이션은 두 가지 방식으로 처리가 가능하다.

1. 오프셋 기반 페이지 네이션 (Offset-based Pagination)

2. 커서 기반 페이지 네이션(Cursor-based Pagination)

 

1. 오프셋 기반 페이지네이션 (Offset-based Pagination)

  • SELECT * FROM BOARDS ORDER BY DATE DESC LIMIT 0, 10;
  • SELECT * FROM BOARDS ORDER BY DATE DESC LIMIT 10, 10;
  • SELECT * FROM BOARDS ORDER BY DATE DESC LIMIT 20, 10;

 

위의 쿼리는 ORACLE이 아닌 MYSQL에서 작동하는 쿼리이다.

아쉽게도 오라클에서는 LIMIT을 사용할 수 없으니 ROWNUM을 이용하여 위의 쿼리와 같은 결과가 나오게 출력해봤다.

  • SELECT * NUM, A.* FROM ( SELECT ROWNUM RNUM FROM BOARDS A ORDER BY DATE DESC ) WHERE RNUM BETWEEN 0 AND 10
  • SELECT * NUM, A.* FROM ( SELECT ROWNUM RNUM FROM BOARDS A ORDER BY DATE DESC ) WHERE RNUM BETWEEN 10 AND 20
  • SELECT * NUM, A.* FROM ( SELECT ROWNUM RNUM FROM BOARDS A ORDER BY DATE DESC ) WHERE RNUM BETWEEN 20 AND 30

 

 

쿼리를 해석해보면 최신글 0~10, 10~20, 20~30을 조회한다.

이렇게 사용하면 1,2,3의 페이지를 클릭하여 다음 콘텐츠를 보는 가장 쉽고 편리한 방식이다. 

하지만 페이스북이나 동시다발적으로 글이 많이올라오는 웹사이트의 경우 다음과 같은 문제가 생길 수 있다.

 

 

문제점1) 페이지 요청 사이에 데이터 변화가 있는 경우 중복 데이터 발생

 

 SNS성향이 짙은 페이스북이나 인스타그램 등은 CRUD(생성,조회,수정,삭제)가 빈번하게 난다. 여기서 오프셋기반의 페이지네이션을 사용한다면 중복 데이터가 발생하는 문제가 생긴다.

 

 예를 들어 1페이지에 20개의 게시물을 불러와 1페이지를 보고있는 사이 다른 유저가 5개의 게시글을 새로 올린다면 유저가 2페이지를 눌렀을 시, 1페이지에서 이미 보았던 20개의 게시물 중 마지막에 있던 5개의 게시물을 다시 2페이지에서 만나게 된다. (등록일 기준으로 내림차순)

 

 

문제점2) 대부분의 데이터베이스에서 OFFSET쿼리의 퍼포먼스 이슈

 

 아주 극단적으로 1000억번째 페이지에 있는 값을 찾고 싶다면 OFFSET쿼리에 매우 큰 숫자가 들어가게 된다.

 그런데 offset값이 올라갈 수록 쿼리의 퍼포먼스는 이에 비례하여 떨어지게 되어있다.

 

Faster Pagination in Mysql – Why Order By With Limit and Offset is Slow?

 

Why Order By With Limit and Offset is Slow - Faster Pagination in Mysql

Paging using LIMIT and OFFSET clauses in MySQL can be very slow. In this article we describe the seek method that allows a faster, more stable paging performance.

www.eversql.com

위 글은 왜 Limit/Offset을 사용하면 점점 속도가 느려지는지 이유와 방법에 대해 설명하고 있다.

 

2. 커서 기반 페이지네이션 (Cursor-based Pagination)

 

위의 오프셋 기반 페이지네이션을 보완하는 커서 기반 페이지네이션은 

리스트를 조회할 때 내가 읽은 마지막 요소를 알려줌으로써 그 뒤의 값을 조회하는 것을 의미한다.

 

즉, 오프셋 기반 페이지네이션은 우리가 원하는 데이터가 "몇 번째" 있다는 데에 초점을 둔다면

커서 기반 페이지네이션은 우리가 원하는 데이터가 "어떤 데이터의 다음"에 있다는 데에 집중한다.

 

  • SELECT * FROM BOARDS ORDER BY DATE LIMIT 10;
  • SELECT * FROM BOARDS WHERE DATE <{이전에 조회한 마지막 날짜} ORDER BY DATE DESC LIMIT 10;

 

위의 쿼리를 해석하면 최신 10개의 글을 조회하고 이후에는 이전에 조회한 마지막 날짜보다 지난 10개를 가져오게 된다.

 

DATE가 제일 최신인 1~30개의 글이 있다 생각하면 

최초에는 30~21 게시글이 조회된다. 두번째 조회 시 WHERE 절에 21번째 게시글을 넣게 되어  게시글 20~11이 조회된다. 세번째에도 11번째 게시글을 넣게되면 10~1이 조회된다.

 

결론

 동일한 레코드가 중복되어도 상관없고 데이터 양이적으면 LIST의 페이지네이션은 오프셋 기반으로 구현하는게 좋다.

그 외에는 기본적으로 커서 기반 페이지네이션을 사용하는것이 좋다. (우선 커서 기반을 바탕으로 생각하고 후에 오프셋 기반으로 해도 적절한지 생각해보라는 이야기)

 

 

 

 

 

<참고자료>

velog.io/@minsangk/%EC%BB%A4%EC%84%9C-%EA%B8%B0%EB%B0%98-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98-Cursor-based-Pagination-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0