특수한 경우에 페이징을 구현함에 있어 괜찮은 속도 개선을 만들어낸 경험이 있어 정리해보려 한다.
0. 테이블 2개의 페이징
보통 페이징을 시도하는 테이블은 1개가 정석인데, 2개로 요청이 들어왔다면 어떻게 쿼리를 짜야할까? 게시물과 댓글 각각 테이블로 데이터를 관리하고 있는 상태에서 게시물, 댓글 모두 등록시간순으로 정렬해 한꺼번에 리스팅 해주면 좋겠다는 요건이 나왔다. 일단, 강력하게 게시물, 댓글을 탭을 나눠 따로 조회했으면 좋겠다고 주장했으나 먹히지 않았다.
1. UNION 페이징
--UNION PAGING
SELECT data, register_time FROM board --게시물
UNION
SELECT data, register_time FROM comment --댓글
ORDER BY register_time DESC
LIMIT 10;
일단 두 테이블이 UNION 되고, 등록시간 순으로 정렬을 싹 하고 10개를 꺼낸다. 허나, 각자의 테이블에 register_time이 인덱스 스캔을 할 수 있다고 하더라도, UNION을 선행 후 ORDER BY 하는 경우에는 인덱스 스캔을 하지 못 하게 된다. 데이터에 따라 엄청난 속도 이슈가 생긴다. 가만히 생각해보다 어차피 정렬 상 10개를 가져오는 거라면, 게시물 최대 10개, 댓글 최대 10개를 가져온 뒤 그중에서 10개만 꺼내면 된다는 생각이 들었다. (= 두 개를 UNION 해서 10개의 데이터를 가져올 때 그 10개의 데이터가 전부 게시물일 수도 있고, 전부 댓글일 수도 있기에 각각 최대 10까지만 구해도 전체 데이터를 가져옴에 문제가 없다)
2. 개선된 UNION 페이징
--NEW UNION PAGING
SELECT * FROM (
SELECT data, register_time FROM board ORDER register_time DESC LIMIT 10
) ordered_board --게시물
UNION
SELECT * FROM (
SELECT data, register_time FROM comment ORDER register_time DESC LIMIT 10
) ordered_comment --댓글
ORDER BY register_time DESC
LIMIT 10;
이렇게 실행하면 엄청나게 속도 개선이 된다. 이 페이징을 시도하기 위해서는 커서페이징 기법으로 시도해야 하며, 2페이징을 진행할 때는 UNION에 해당되는 각 테이블에도 같은 조건으로 커서 필터링을 해줘야 한다. (실제 서비스 상에 개선했을 때는 헤비유저 기준으로 5초 => 0.3초 정도까지 개선이 되기도 했다.)
3. 더 좋은 방법
급하게 하다 보니 일단 쿼리로만 개선을 생각했으나, 제대로 이 부분을 개선한다면, 테이블 구조 자체를 바꾸는 것이 맞다고 본다. 게시물 테이블, 댓글 테이블에 있는 데이터를 활용하여 게시물+댓글 테이블을 새로 만들어 계속 3개의 테이블에 데이터를 쌓아 놓아야한다. 그리고 새로 만든 테이블에서 페이징을 시도해야 한다. 대신, 싱크가 맞아야 하기 때문에, TRIGGER 혹은 로직상 항상 서로의 테이블을 신경 써야 한다. (사실, 가장 좋은 방법은 카테고리를 명확하게 하는 것, 게시물과 댓글이 꼭 섞여야 할까? 의문이다 ㅠㅠ)
'Deep Dive Series > Paging' 카테고리의 다른 글
[페이징 톺아보기 3] 두번째 페이징이 잦을때, IN절 페이징 (0) | 2021.08.07 |
---|---|
[페이징 톺아보기 1] 가장 효율적인 커서 기반 페이징 (0) | 2021.08.02 |
[페이징 톺아보기 0] Why, How, What is 'Paging'? (0) | 2021.07.31 |