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)를 반환해준다.
커서 기반 페이징이 간단한 것 같았는데 아래 글을 보니 복잡한 경우에서는 고려해야 될 부분이 많은 것 같다.. 갈 길이 멀다.
[참고]
Real MySQL 8.0
'Spring' 카테고리의 다른 글
[Spring] 커스텀 Validator 적용 (ConstraintValidator) (0) | 2023.11.27 |
---|---|
[Spring] 트랜잭션과 @Transacional 사용하는 이유 (0) | 2023.05.02 |
Servlet과 Servlet Container (5) | 2023.03.25 |
[Spring Boot] 스프링 시큐리티 OAuth2.0 로그인 (KAKAO) (0) | 2023.02.15 |
[Spring Rest Docs] Rest API 문서화 (0) | 2023.01.28 |