개요
대용량 데이터베이스에서 페이지 단위로 데이터를 조회할 때, 흔히 사용하는 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;
- 원리
- 이전 페이지 마지막 데이터(lastInsTime, lastHistoryId)를 커서로 사용
- 커서 이후의 데이터만 조회
- 순서를 보장하면서 페이지 데이터를 가져옴
- 장점
- 대용량 데이터에서도 성능 일정
- 순서 안정성 확보
- 실시간 데이터 조회, 무한 스크롤에 적합
- 단점
- 특정 페이지로 점프 어려움
- 커서 정보 클라이언트 또는 서버에서 관리 필요
커서 페이징 vs OFFSET 페이징 비교
| 구분 | 커서 페이징 | OFFSET 페이징 |
| 성능 | 일정, 대용량 처리에 강함 | OFFSET 값 커질수록 느려짐 |
| 페이지 접근 | 이전 페이지 커서 필요 | 특정 페이지 바로 접근 가능 |
| 순서 안정성 | 안정적 | 삽입/삭제 시 순서 변화 가능 |
| 사용 사례 | 무한 스크롤, 실시간 데이터 | 일반 페이지 조회, 보고서 |
실무 적용 예시 (Spring + JPA)
List<EventDto> events = eventsQueryRepository.findPageByCursor(
20, // 페이지 크기
lastInsTime, // 마지막 데이터 시간
lastHistoryId // 마지막 데이터 ID
);
- ins_time + history_id를 기준으로 다음 페이지 데이터 조회
- OFFSET 기반 쿼리보다 대규모 데이터 조회 성능 안정화
회고 및 팁
- 대용량 로그, 이벤트, 거래 기록 등 빠르게 쌓이는 테이블에서는 OFFSET보다 커서 페이징이 훨씬 효율적임을 체감했습니다.
- 커서 기반으로 설계하면 무한 스크롤 구현이 자연스러워지고, 서버 부담도 줄어듭니다.
- 단, 특정 페이지 바로 접근이 필요한 경우에는 커서 페이징 + 캐시 전략을 고려하면 좋습니다.
'프로그래밍 > Database' 카테고리의 다른 글
| 💻 [MongoDB] Docker로 Replica Set 구성 (2) | 2025.05.08 |
|---|---|
| 💻 [MongoDB] 성능 최적화 실전 가이드 – 인덱스, explain, count 주의사항 (0) | 2025.05.07 |
| 💻 [MongoDB] 고급 분석 연산자 - 분포, 통계, 병렬 (0) | 2025.05.04 |
| 💻 [MongoDB] 고급 파이프라인 연산자 (0) | 2025.05.02 |
| 💻 [MongoDB] Aggregation 고급 함수 (0) | 2025.05.02 |