Files
document-haness/docs/n+1liner/tech-log-studio/jpa-feed-query-performance/reference/reference-keyset-pagination-design.md
T

4.1 KiB

id, kind, slug, title, topic, project, status, studio
id kind slug title topic project status studio
06788903-3dfa-4f70-b159-f1224384fd0b REFERENCE keyset-pagination-design Keyset Pagination 설계 기준 JPA 피드 조회 성능 Liner N + 1문제 게시 전 https://hyeonworks.com/studio/documents/06788903-3dfa-4f70-b159-f1224384fd0b/edit

Keyset Pagination 설계 기준

OFFSET은 건너뛸 행까지 만든 뒤 버린다. keyset은 이전 페이지의 마지막 행을 커서로 삼아 그 지점 이후만 읽는다. 다만 커서와 같은 순서의 정렬키 인덱스가 있어야 이 이점이 생긴다.

관계

  • Visibility OR이 Keyset Index를 깨뜨린 문제 이 기준의 전제가 깨지는 조건을 확인한 기록이다.
  • Feed Pagination은 Keyset을 사용한다 이 기준에서 나온 결정이다.
  • PostgreSQL Query Plan 측정 기준 깊이별 비용을 실행계획으로 확인하는 기준이다.

목적

무한 스크롤에서는 뒤쪽 페이지일수록 OFFSET이 커진다. 정렬키 인덱스가 있어도 건너뛸 튜플을 훑어야 하고, 깊으면 전량 스캔과 정렬로 떨어진다.

페이지 깊이와 무관하게 읽는 행수를 일정하게 유지하려면 커서 방식이 필요하다.

규칙

1. 커서에 정렬키를 모두 담는다

정렬이 여러 컬럼이면 커서도 같은 컬럼을 모두 가진다. 앞 컬럼만 커서로 쓰면 값이 같은 행이 있을 때 경계에서 빠지거나 중복된다.

2. tie-break 컬럼을 정렬과 커서에 넣는다

정렬키에 중복이 있을 수 있으면 유일한 컬럼을 마지막 정렬키로 더한다. 커서에도 같이 담는다.

3. 정렬키, 커서, 인덱스의 컬럼과 방향을 일치시킨다

셋 중 하나라도 어긋나면 인덱스가 순서를 주지 못해 Sort가 다시 생긴다. 방향까지 같아야 한다.

4. 정렬키 전용 인덱스를 확인한다

선두 컬럼이 다른 인덱스는 이 쿼리에 쓰이지 않는다. 필터가 없는 정렬 쿼리라면 정렬키만으로 된 인덱스가 필요하다.

인덱스가 없으면 keyset도 전량을 스캔한다. keyset 문법이 아니라 인덱스가 비용을 줄인다.

5. 깊이별로 훑은 행을 측정한다

OFFSET은 offset에 페이지 크기를 더한 만큼 훑는다. keyset은 페이지 크기만큼 훑는다. 훑은 행은 Limit 하위의 actual rows로 읽는다.

한 페이지만 재면 차이가 보이지 않는다. 깊이를 바꿔 가며 곡선으로 확인한다.

6. 결과가 OFFSET과 같은지 검증한다

커서로 넘긴 페이지가 같은 순서의 같은 행을 반환하는지 대조한다. 페이지 크기, 순서, 식별자를 모두 확인한다.

7. 필터를 얹으면 전제가 깨질 수 있다

선택 조건이 여러 분기로 갈리면 플래너가 분기별로 스캔한 뒤 합치면서 정렬 순서를 잃는다. 이때 Sort가 다시 나타난다.

필터가 있는 keyset은 필터를 포함한 인덱스 설계나 쿼리 분해가 함께 필요하다.

8. 정렬키에 null이 있을 수 있는지 먼저 정한다

정렬키가 nullable이면 null의 정렬 위치와 커서 표현을 정의해야 한다. 이 판단을 미루면 커서 비교식이 경계에서 어긋난다.

적용 조건

  • 무한 스크롤이나 깊은 페이지를 지원할 때
  • 정렬 순서가 고정돼 있고 인덱스를 만들 수 있을 때
  • 전체 페이지 수가 필요하지 않을 때

예외

  • 임의 페이지 점프가 필요하면 커서만으로는 부족하다. OFFSET을 함께 두거나 다른 탐색을 설계한다.
  • 전체 건수를 화면에 표시해야 하면 count를 별도로 다룬다. 커서 결과에는 전체 건수가 없다.
  • 정렬 기준이 자주 바뀌면 기준마다 인덱스가 필요하다. 인덱스 수와 쓰기 비용을 함께 본다.

예시

  • OFFSET 훑은 행 : offset + 페이지 크기
  • keyset 훑은 행 : 페이지 크기 (깊이 무관)
  • 커서 : (정렬키, tie-break) 조합
  • 전제 인덱스 : 정렬키와 같은 컬럼·같은 방향
  • 인덱스 없는 keyset : 전량 스캔, 이점 없음
  • 필터 추가 : 분기가 갈리면 Sort 재등장