FreeHand

OFFSET vs Cursor Pagination 정리 본문

Database/RDB

OFFSET vs Cursor Pagination 정리

Jinn 2026. 3. 30. 14:38

 

목록 조회를 구현할 때 가장 많이 사용하는 방식은 크게 두 가지다.

 

  • OFFSET 기반 페이징
  • Cursor 기반 페이징

OFFSET 방식


SELECT *
FROM post
ORDER BY id DESC
LIMIT 20 OFFSET 10000;

 

OFFSET 방식은 위 쿼리처럼 LIMIT와 OFFSET을 사용해서 조회하는 방식이다.
id로 내림차순 정렬하고 10000개 건너뛰고 그다음 20개를 조회한다.

즉 아래와 같이 동작한다.

 

  1. 정렬 수행
  2. 10000개 row 읽음
  3. 10000개 버림
  4. 다음 20개 row 반환

20개를 조회하기 위해서 10020개를 조회한 것이다.

OFFSET이 커질수록 즉 페이지가 뒤로 갈수록 느려진다는 의미이다.

 

Cursor 방식


SELECT *
FROM post
WHERE id < 10000
ORDER BY id DESC
LIMIT 20;

 

Cursor 방식은 현재 위치(커서)를 기준으로 WHERE 조건절을 사용해서 조회하는 방식이다.

id = 10000을 찾아서 바로 20개 행만 읽는다.

항상 동일하게 20개만 읽으므로 뒤 페이지로 가도 조회 성능도 동일하다.

 

더 생각해 볼 점


인덱스 사용

SELECT *
FROM members
WHERE id < 10000
ORDER BY id DESC
LIMIT 20;

이경우에는 id 컬럼이 PK이므로 PK 인덱스를 사용해서 바로 20개만 조회할 수 있다.

 

SELECT *
FROM members
WHERE age < 30
ORDER BY age DESC
LIMIT 20;

만약 age 컬럼에 인덱스가 없으면 테이블 전체를 스캔하며 age < 30인 행을 찾고 정렬 후 20개를 반환한다.

즉 커서로 사용하는 컬럼에 인덱스가 없으면 결국 Full Table Scan이 발생한다.

 

복합 커서

SELECT *
FROM members
WHERE (age < {age})
ORDER BY age DESC, id DESC
LIMIT 20;

이렇게 작성하면 만약 age가 30인 회원이 여러 명인 경우, 이미 커서(age) 값이 30이므로 다음 페이지 조회에서 age가 30인 나머지 회원들은 나오지 않는다.

SELECT *
FROM members
WHERE (age < {age}) OR (age = {age} AND id < {id})
ORDER BY age DESC, id DESC;

그래서 위와 같이 (age, id) 복합 커서를 사용해야 누락 문제를 피할 수 있다.

이때도 마찬가지로 (age, id) 복합 인덱스가 필요하다.

 

UI / UX

OFFSET 방식은 페이지당 개수와 페이지 번호를 통해 조회를 할 수 있다. 즉 특정 페이지 번호로 이동이 가능하다.

반면 Cursor 방식은 "조회한 마지막 위치"에서 이어서 조회하는 성격이므로 특정 페이지로 이동할 수 없다.

 

따라서 페이지 번호가 있는 조회에서는 OFFSET이 더 적합하고, 마지막 위치에서 추가로 더 조회하는 무한 스크롤에는 Cursor가 적합하다고 볼 수도 있다.

 

 

'Database > RDB' 카테고리의 다른 글

옵티마이저와 실행계획  (4) 2024.09.24
[MySQL] 데이터 타입과 형 변환  (0) 2023.11.05
[MySQL] WITH절과 CTE  (0) 2023.11.05
[MySQL] DELETE  (0) 2023.11.01
[MySQL] UPDATE  (0) 2023.11.01