Paging
: findAll(), findBy~() 같은 조회를 할 때 많은 데이터가 조회 된다. 한번에 모든 데이터를 가져올 수 없고, 사용자 또한 한번에 모든 데이터를 볼 수도, 필요도 없기에 데이터를 가져올 때 한번에 몇 개, 어느 페이지부터와 같은 처리를 해주는 것을 페이징이라고 한다.
페이징 처리에는 offset 방식과 cursor(No offset) 방식이 있다. 보통 두 방식 모두 사용 한다. 특히 cursor방식은 모바일이 발달됨에 따라 필요성이 많아지고있다. (필수라고 생각한다.)
Offset 방식
offset 방식은 위처럼 페이지번호(offset)과 페이지 사이즈(limit)을 기반으로 한다. 아주 기본적인 페이징 방법이지만 고려해야할 사항이 두가지 있다.
- 중간에 데이터가 추가되면, 다음 페이지에 갔을 때 이전 페이지에서 봤던 값이 추가될 수 있다. (혹은 누락될 수 있다.)
- 만약 100,000번째 데이터 값부터 10개를 가져 와야한다면, 100,010개를 다 읽고 앞의 100,000를 버리는 형태로 진행이 되므로 뒤로 갈 수록 버리지만 읽어야 할 행의 개수가 많아 느려진다.
1번을 자세히 설명하자면 게시판 예시를 생각해볼 수 있다. 작성 최신순으로 게시판을 보고있었다. 그런데 두번째 페이지로 넘어간다면? 첫번째 페이지에서 봤던 게시글이 두번째 페이지에서 중복으로 조회된다.
이유는 offset방식은 사용자가 마지막으로 읽은 값을 고려하지 않고, 조회할 때마다 sort기준(작성일 desc등)을 가지고 조회를 한다음에 페이지를 쪼개는 방식을 사용하기 때문이다.
2번에 대한 퍼포먼스 이슈에대해서는 발생이 잦을까..? 생각할 수 있지만 가능한 케이스가 있으니 첨부하겠다.
offset의 고려사항을 해소하고자, 또 모바일에서는 무한 스크롤방식을 주로 사용하고 중복을 허용하는것이 어색하기에 cursor방식을 사용한다.
//offset 방식 쿼리 SELECT * FROM products ORDER BY updated_at DESC //최신순 OFFSET 10000 //10000번째부터 LIMIT 10 //10개
Cursor 방식
cursor 방식은 클라이언트가 어디까지 가져갔는지를 아는 방식이다. 즉 클라이언트가 id 1000까지 봤으니 1001번부터 10개 내놔! 하는 방식이다.
때문에 request로 클라이언트가 어디까지 읽었는지를 받아야한다.
- cursor 기준 값은 unique해야한다.
SELECT * FROM products WHERE updated_at < '2022-07-20 06:38:12' // '' 값이 클라이언트에가 받은 기준값 ORDER BY updated_at DESC LIMIT 10
그런데 여기에는 함정이 있다. 만약 '2022-08-14 08:53:39' 의 시간으로 update된 producsts가 중복으로 10개가 있다면? 그럼 다음 조회에서는 10개가 누락이 될 것이다. 잘못된 페이징으로 데이터가 유실 된 것이다.
때문에 cursor의 기준이 되는 값은 unique 해야한다.
혹은 unique한 컬럼과 섞어 써야한다. (특히 시간값은 unique한 id값과 섞어써야한다.)
- 2point cursor 쿼리작성시 2 column order by에 따른 조건을 신경쓰라
// (X) WRONG SELECT * FROM products WHERE updated_at < '2022-08-14 08:53:39' and id < 10 ORDER BY updated_at DESC, id DESC LIMIT 10 //(O) RIGHT SELECT * FROM products WHERE (updated_at < '2022-08-14 08:53:39' ) or (update_at = '2022-08-14 08:53:39' and id < 10) ORDER BY updated_at DESC, id DESC LIMIT 10
여기에 또 다시 오류가 있다. 이렇게 2point cursor을 하게되면, id값이 항상 내림차순이 아니라는 것을 알 것이다. (update_at 으로 먼저 내림차순 되고, 중복값들 중에 id로 내림차순 되니까)
때문에 WRONG 처럼 쿼리를 짠다면 id가 10보다 작은애가 없을 수도 있다.
날짜값이 작거나 혹은 같은값들 중에서 두번째 정렬값인 id 보다 작은 애들을 가져와야한다.
- 단점
- 2 Column(point) cursor는 2백만개까지는 offset방식보다 느리다.