💻 [데이터페이스] 대용량 데이터, 페이징 때문에 느려진다면? 커서 페이징으로 속도 UP!
2025. 11. 27. 16:23

개요

대용량 데이터베이스에서 페이지 단위로 데이터를 조회할 때, 흔히 사용하는 OFFSET/LIMIT 기반 페이징은 성능 저하와 순서 불안정 문제를 야기할 수 있습니다.
이를 해결하는 방법으로 커서(Cursor) 페이징이 있습니다. 커서 페이징은 마지막 조회 항목을 기준으로 다음 데이터를 가져오는 방식으로, 무한 스크롤이나 실시간 데이터 조회에 최적화되어 있습니다.


기존 페이징 방식

  • 방식: OFFSET + LIMIT
  • 예시 쿼리:
SELECT *
FROM event_history
ORDER BY ins_time DESC
LIMIT 10 OFFSET 20;

 

  • 장점
    • 특정 페이지 바로 접근 가능
    • 구현이 단순함
  • 단점
    • OFFSET이 커질수록 DB 성능 저하
    • 대량 데이터 조회 시 느려짐
    • 삽입/삭제 등 데이터 변경 발생 시 페이지 순서 흔들림

커서 페이징 방식

  • 방식: 마지막 항목을 기준으로 다음 데이터를 조회
  • 커서 조건 예시
WHERE ins_time < :lastInsTime
   OR (ins_time = :lastInsTime AND history_id < :lastHistoryId)
ORDER BY ins_time DESC, history_id DESC
LIMIT 10;

 

 

  • 원리
    1. 이전 페이지 마지막 데이터(lastInsTime, lastHistoryId)를 커서로 사용
    2. 커서 이후의 데이터만 조회
    3. 순서를 보장하면서 페이지 데이터를 가져옴
  • 장점
    • 대용량 데이터에서도 성능 일정
    • 순서 안정성 확보
    • 실시간 데이터 조회, 무한 스크롤에 적합
  • 단점
    • 특정 페이지로 점프 어려움
    • 커서 정보 클라이언트 또는 서버에서 관리 필요

커서 페이징 vs OFFSET 페이징 비교

구분 커서 페이징 OFFSET 페이징
성능 일정, 대용량 처리에 강함 OFFSET 값 커질수록 느려짐
페이지 접근 이전 페이지 커서 필요 특정 페이지 바로 접근 가능
순서 안정성 안정적 삽입/삭제 시 순서 변화 가능
사용 사례 무한 스크롤, 실시간 데이터 일반 페이지 조회, 보고서

실무 적용 예시 (Spring + JPA)

List<EventDto> events = eventsQueryRepository.findPageByCursor(
        20,                      // 페이지 크기
        lastInsTime,             // 마지막 데이터 시간
        lastHistoryId            // 마지막 데이터 ID
);
 
  • ins_time + history_id를 기준으로 다음 페이지 데이터 조회
  • OFFSET 기반 쿼리보다 대규모 데이터 조회 성능 안정화

회고 및 팁

  • 대용량 로그, 이벤트, 거래 기록 등 빠르게 쌓이는 테이블에서는 OFFSET보다 커서 페이징이 훨씬 효율적임을 체감했습니다.
  • 커서 기반으로 설계하면 무한 스크롤 구현이 자연스러워지고, 서버 부담도 줄어듭니다.
  • 단, 특정 페이지 바로 접근이 필요한 경우에는 커서 페이징 + 캐시 전략을 고려하면 좋습니다.