Real MySQL 단톡방에서 어떤 분이 페이징을 할 때 order by에 대해 질문을 하셨는데 저자분께서 친절하게 답변을 해주셨다. 마침 코드스쿼드에서 미션으로 간단한 게시판 구현을 해보고 있어서 LIMIT 옵션에 대해서 공부를 해보았다.

Real MySQL 11장 LIMIT

더보기

MySQL의 LIMIT는 항상 쿼리의 가장 마지막에 실행되고 LIMIT에서 필요한 레코드 건수만 준비되면 즉시 쿼리를 종료한다.

 

ORDER BY나 GROUP BT, DISTINCT가 인덱스를 이용해 처리될 수 있다면 LIMIT 절은 꼭 필요한 만큼의 레코드만 읽게 만들어주기 때문에 쿼리의 작업량을 상당히 줄여준다.

 

LIMIT는 1개 또는 2개의 인자를 사용할 수 있는데 첫번째 인자에 지정된 위치부터 두번째 인자에 명시된 개수만큼의 레코드를 가져온다.

 

즉 LIMIT(시작 위치, 오프셋)으로 LIMIT 10과 같이 인자가 1개인 경우는 LIMIT 0, 10과 동일하다.

 

참고로 LIMIT의 인자로 표현식이나 별도의 서브쿼리는 사용할 수 없다.

 

LIMIT 쿼리에서 주의할 점은 몇 건을 읽을건지보다 그 결과를 만들어 내기 위해 어떤 작업을 했는지다.

 

SELECT * FROM salaries ORDER BY salary LIMIT 2000000, 10;

 

위의 쿼리는 salaries 테이블을 처음부터 읽으면서 2000010건의 레코드를 읽어서 2000000건은 버리고 마지막 10건만 반환한다. 그래서 이런 경우 WHERE 조건으로 읽어야 할 위치를 찾고 그 위치에서 10개만 읽는 형태의 쿼리를 사용하는 것이 좋다.

 

댓글 리스트 조회시 Request Parameter로 startId와 읽어올 개수 size를 전달 받는다. 더보기 버튼을 누르면 다음 댓글들을 더 보여주도록 구현을 했다.

 

이전에 LIMIT 조건의 주의점을 보긴 했었는데 까먹고 있다가 다시 책을 보면서 아차 싶었다. LIMIT에 시작 위치를 주는 경우 그 값이 점점 커진다면 성능이 저하될 수 있으니 WHERE 조건을 잘 활용하도록 주의해야겠다. (물론 지금은 전체 게시글이 아닌 특정 게시글의 댓글을 조회하는 것이기 때문에 큰 문제는 없을 것 같다.)

+ ORDER BY가 인덱스를 이용해 처리될 수 있으면 LIMIT 시 쿼리의 작업량을 많이 줄여준다.

 

 

더보기 버튼을 누르는 식으로 구현을 했기 때문에 댓글 리스트를 읽어올 때 전체 count를 불필요하게 읽을 필요가 없다. 대신 다음 댓글이 있는지 여부를 확인하기 위해 size + 1만큼 읽어온다. Dto로 변환할 때는 subList()를 사용해서 size 개수(마지막 페이지의 경우 size 이하의 개수)만큼 잘라주었다. (급하게 구현해서 네이밍이 맘에 안 든다..)

 

 

응답 결과는 다음처럼 댓글 리스트와 다음 댓글 여부(hasNext)를 반환해준다.

 

커서 기반 페이징이 간단한 것 같았는데 아래 글을 보니 복잡한 경우에서는 고려해야 될 부분이 많은 것 같다.. 갈 길이 멀다.

 

커서 기반 페이지네이션 (Cursor-based Pagination) 구현하기

사실 처음에는 이 주제로 포스트를 쓰려고 했던건 아니고 Apollo GraphQL 에서 커서 기반 페이지네이션 구현 을 주제로 글을 쓰려고 했습니다. 그런데 막상 찾아보니 백엔드-프론트엔드를 함께 고려

velog.io

 

 

 

 

[참고]

Real MySQL 8.0

+ Recent posts