MySQL에서 가장 많이 사용하는 페이징 방식은 LIMIT OFFSET 구조다.
하지만 데이터가 많아질수록 이 방식은 점점 느려지는 문제가 있다.
특히 페이지 번호가 커질수록 쿼리 속도는 급격히 떨어진다.
이 현상은 단순한 인덱스 문제라기보다 OFFSET 구조 자체의 특성 때문이다.
1. 일반적인 OFFSET 페이징 쿼리
가장 흔한 형태의 페이징 쿼리는 다음과 같다.
SELECT id, title, created_at
FROM post
ORDER BY created_at DESC
LIMIT 20 OFFSET 10000;
이 쿼리는 “10000개를 건너뛰고 20개를 가져온다”는 의미다.
문제는 MySQL이 실제로는 데이터를 건너뛰는 것이 아니라 읽어야 한다는 점이다.
2. OFFSET이 느려지는 구조
MySQL은 OFFSET을 처리할 때 다음과 같은 과정을 거친다.
- 정렬 기준으로 인덱스 또는 테이블 스캔
- OFFSET 수만큼 데이터를 읽음
- 그 이후 LIMIT 수만큼 반환
즉 OFFSET이 10000이면 10000개의 데이터를 읽고 버린 뒤 결과를 반환한다.
OFFSET이 커질수록 불필요한 읽기 비용이 계속 증가한다.
이 구조 때문에 페이지 번호가 뒤로 갈수록 쿼리 속도가 계속 느려진다.
3. 인덱스가 있어도 느려질 수 있는 이유
ORDER BY 컬럼에 인덱스가 있어도 OFFSET 비용은 사라지지 않는다.
인덱스 스캔을 하더라도 OFFSET 만큼은 반드시 읽어야 하기 때문이다.
특히 다음 조건이 겹치면 성능 저하가 더 커진다.
- OFFSET 값이 수만 이상
- 정렬 컬럼이 인덱스를 타지 않는 경우
- JOIN이 포함된 쿼리
- SELECT * 사용
4. Keyset Pagination (Seek 방식)
OFFSET 문제를 해결하기 위한 대표적인 방법은 Keyset Pagination이다.
OFFSET 대신 마지막 조회 데이터를 기준으로 다음 페이지를 가져오는 방식이다.
SELECT id, title, created_at
FROM post
WHERE created_at < '2024-01-01 10:00:00'
ORDER BY created_at DESC
LIMIT 20;
이 방식은 OFFSET처럼 앞 데이터를 모두 읽지 않는다.
인덱스 범위 검색으로 바로 다음 페이지를 가져올 수 있다.
데이터가 수천만 건 이상인 서비스에서는 이 방식이 일반적으로 더 효율적이다.
5. OFFSET 방식이 괜찮은 경우
모든 상황에서 OFFSET이 나쁜 것은 아니다.
데이터 규모와 사용 패턴에 따라 충분히 사용할 수 있다.
- 데이터가 수만 건 이하인 경우
- 페이지 번호 이동이 필요한 UI
- 관리자 화면
대규모 서비스에서는 Keyset Pagination을 사용하고,
관리 도구나 내부 시스템에서는 OFFSET을 사용하는 방식이 일반적이다.
6. 실무에서 자주 사용하는 전략
- 목록 API → Keyset Pagination
- 관리 페이지 → OFFSET Pagination
- 검색 결과 → OFFSET + 제한
또는 최근 데이터만 조회하는 서비스라면 OFFSET 대신 “최근 N개 조회” 구조로 설계하기도 한다.
한 줄 요약
OFFSET 페이징은 앞 데이터를 모두 읽고 버리는 구조이기 때문에 페이지가 뒤로 갈수록 느려진다. 대용량 데이터에서는 마지막 조회 값을 기준으로 검색하는 Keyset Pagination 방식이 더 효율적이다.
댓글 0