0. 스크롤 페이징이 점점 느려져요!
페이징은 점점 느려질 수 있다. 테이블의 크기가 커지면서 느려질 수도 있고, API 로직이 복잡해지면서 느려질 수도 있다. 하지만, 애초에 OFFSET, LIMIT으로 조회하는 방식은 앞에서 읽었던 행을 다시 읽어야하기 때문에 뒤로 갈수록 느리다. 그리고 OFFSET, LIMIT은 인덱스 스캔과는 전혀 상관없는 명령어기때문에 인덱스의 이점을 살릴 수도 없다. 이 때, 개선 가능한 방법이 No-Offset 페이징 혹은 커서 페이징이라고 불리우는 페이징 기법이다.
1. 속도가 빠른 커서 페이징
"커서 기반 페이징이 가장 효율적인 방법이며, 가능한 항상 사용되어야한다."라고 타임라인 기능을 무한스크롤 페이징으로 만든 페이스북 개발자는 말했다. 그만큼 커서 페이징은 가장 효율적인 페이징 방법이라고 생각할 수 있다. 단, 활용하기 위해 2가지 조건이 전제되어야한다.
- 커서로 활용할 컬럼은 인덱스가 설정되어 있어야한다.
- 커서로 활용할 컬럼은 고유해야한다.
이 조건을 만족하는 컬럼은 '시퀀스 ID', 'PK ID' 등이 있고, 이를 활용해 충분히 구현이 가능하다. 시간을 활용해서도 구현이 가능한데 그에 관련한 크나큰 오류는 뒤에 설명하기로 한다. 커서페이징은 속도 면에서만 이점이 있는 것은 아니다. 장점이 몇 개 더 있는데 2가지를 더 설명해보려고 한다.
2. 실시간 데이터를 가장 잘 꺼낼수 있는 커서 페이징
위의 사진을 보며, OFFSET 페이징으로 1페이징을 했다고 가정해보자. 2페이징에는 가장 왼쪽처럼 997, 996, 995 번의 데이터를 기대하게 된다. 하지만, 데이터가 추가되거나 삭제하면, 2페이징에는 알맞는 데이터가 매칭되지 않음을 확인할 수 있다. 중간 케이스에는 998번이 중복으로 리스팅될 것이며, 오른쪽 케이스는 997번이 누락될 것이다.
만약 이 상황에 커서 페이징이라고 한다면, 1페이징 뒤에 998이라는 커서를 얻게 되고, 다음 페이징에는 998 이후의 3개의 데이터를 가져오기 때문에 누락이 발생할 수가 없다. (물론, 데이터가 추가되거나, 삭제에 따른 기존 데이터 배열의 갱신 문제는 별도로 봐야한다.)
--OFFSET 페이징 SELECT data FROM board ORDER BY data_num DESC LIMIT 3 OFFSET 0; --1페이징 SELECT data FROM board ORDER BY data_num DESC LIMIT 3 OFFSET 3; --2페이징 --커서 페이징 SELECT data FROM board ORDER BY data_num DESC LIMIT 3; -- 1페이징 SELECT data FROM board WHERE data_num < 998 ORDER BY data_num DESC LIMIT 3; -- 2페이징
3. 어느 시점을 기준으로 위 아래 스크롤 페이징이 가능한 커서 페이징
보통 페이징은 방향성을 한 쪽으로 가지기 마련인데, 특수한 경우에 방향성을 양쪽으로 가져가야하는 경우가 있다. 예를 들어, 검색과 스크롤을 연계할 때 그러한데, 카카오톡 채팅 검색을 생각해보면, 검색된 채팅 메시지의 시점으로 위로도 페이징이 되고, 아래로도 페이징이 됨을 확인할 수 있다. 스크롤 방향성과 커서 값만 잘 설정해주면 어렵지 않게 위아래 커서 페이징이 가능하다.
4. 커서 컬럼 값이 1개로 애매할때
커서 값을 꼭 1개의 값만 활용하진 않아도 된다. 두개의 커서값을 활용해도 되고, 커서 값을 CONCAT으로 합쳐 활용할 수도 있다. 허나, 한 포스팅에 의하면, Cursor 1 Column은 미친듯이 빠르지만, Cursor 2 Column 방식은 Offset 보다 초반 페이징에는 불리할 수도 있는 점을 알아두어야한다.
5. 시간값을 활용한 커서 페이징
기존 테이블 상에 마땅한 커서가 없고, 그나마 시간값이 있어 활용하는 경우가 있다. 실제로, 서비스 운영하면서, 시간값으로 커서 페이징을 구현한 적이 있는데, 이는 엄청난 오류를 만들어냈다. 바로, 동시간대에 insert 되는 데이터가 skip이 된다는 오류였다. 연월일시분초라는 시간으로 기록하고 있었다면, 1초 안에 동시에 작성된 모든 데이터가 skip 될 수 있다는 것을 의미한다. 밀리초(ms) 까지 시간을 나타냈다면 더 좋지만 그렇다고 완벽하게 skip이 안 된다고는 말할 수 없다. 결국, Cursor 2 column 방식을 채택했다. 위에 나온 표처럼 Cursor2 Column 이 무조건 Offset 보다 초기 페이징에 느린 것은 아니었어서 그렇게 진행했다. 여튼, 시간값을 행여나 커서로 활용하려면, 최소 밀리초(ms) 까지는 되는 시간 값을 활용해야한다.
'Deep Dive Series > Paging' 카테고리의 다른 글
[페이징 톺아보기 3] 두번째 페이징이 잦을때, IN절 페이징 (0) | 2021.08.07 |
---|---|
[페이징 톺아보기 2] 두 테이블의 유니온 페이징 (정석이 아니라 선택) (0) | 2021.08.02 |
[페이징 톺아보기 0] Why, How, What is 'Paging'? (0) | 2021.07.31 |