안녕하세요!
React와 Node를 학습하기 위해 개인 토이 프로젝트로 블로그 서비스를 개발하면서 겪었던 페이징 쿼리 성능 문제와 그 문제를 어떻게 해결했는지에 대해 공유하려고 합니다.
페이징 쿼리 성능 이슈
주로 게시물을 조회할 때, 페이징 쿼리를 사용하는데요, 여기서 직면한 문제를 소개하겠습니다.
페이지 후반으로 갈수록 속도 저하 문제
1, 2페이지와 같은 초반 페이지는 큰 문제가 없었지만, 페이지가 뒤로 갈수록 조회 응답 시간이 점차 느려지는 문제가 발생했습니다. 10만 개의 더미 데이터에서도 느렸고, 100만 개 이상으로 늘어날수록 이 문제는 더욱 심각해질 것이라고 생각했습니다.
문제 분석
페이징 처리 과정에서 Limit과 Offset 쿼리를 사용했을 때, Offset 값이 작을 때는 데이터를 빠르게 받아오지만, Offset 값이 클수록 데이터를 받아오는 데 시간이 오래 걸린다는 것입니다.
저는 OFFSET 에 지정된 갯수부터 LIMIT 인 page size 만큼만 조회할 것이라 기대했지만 실제로는 그렇지 않았습니다.
OFFSET 기반 페이징은 0번 OFFSET 부터 지정된 OFFSET 까지 데이터 조회를 모두 수행한 후, 결과를 제외한 나머지를 버리게 됩니다.
그러다 보니, OFFSET 값이 커질수록 성능이 저하됩니다.
10만 개의 데이터에서는 685ms가 걸렸지만, 데이터가 많아질수록 응답 시간은 더 길어질 것입니다.
해결 방법
해결 방법으로는 No-Offset 방법과 커버링 인덱스 방법이 있습니다.
- No-Offset → 커서 기반 페이징 처리라고도 합니다. 커서(PK)를 기준으로 LIMIT 개수만큼 불러오기 때문에 훨씬 효율적으로 동작합니다.
- 커버링 인덱스 → 쿼리를 충족시키는 데 필요한 모든 데이터를 갖고 있는 인덱스를 말합니다. SELECT, WHERE, ORDER BY, GROUP BY, HAVING 절에서 사용되는 컬럼들을 포함한 인덱스를 생성하면, 인덱스에서 직접 결과를 찾을 수 있어, 데이터를 실제 테이블에서 조회하지 않고도 쿼리를 처리할 수 있습니다. 이로 인해 데이터 블록에서 필요한 행만을 조회하게 되어 성능상의 이점을 얻을 수 있습니다.
다음과 같이 ID, userID, bbsAvailable로 복합 인덱스를 설정하였습니다.
가장 변경이 적은 컬럼들로 인덱스를 설정하는 것이 좋다고 판단하였습니다.
(before) (after)
(before) 쿼리 같은 경우 앞에 설정한 인덱스를 타지 않는 컬럼들이 있습니다. 그렇기 때문에 일반적인 쿼리가 실행되서 조회 성능이 저하 됩니다.
(after) 쿼리의 경우, 서브쿼리에서 SELECT, WHERE, ORDER BY 조건에 사용된 컬럼들이 모두 앞서 설정한 인덱스에 포함되어 있습니다. 이로 인해 커버링 인덱스가 적용되어 bbsID를 빠르게 조회할 수 있게 됩니다.
결과
685ms에서 50ms로 약 13배 조회 성능을 개선하였습니다.
단점
인덱스를 사용하는 것이기 때문에 단점이 존재합니다.
- 모든 쿼리의 항목이 인덱스에 포함되어야 하기 때문에 많은 인덱스가 필요합니다.
- 데이터 양이 많아질수록 NoOffset에 비해 성능이 뒤처집니다.
'project > etc' 카테고리의 다른 글
setInterval로 DB 갱신 여부, WebSocket으로 개선하기 (0) | 2025.02.25 |
---|---|
AWS 비용, 미니 PC로 해결하기 (0) | 2025.02.21 |
최단 경로 성능 개선 (0) | 2025.02.19 |