GH GambleHub

페이지 네이션 및 커서

1) 페이지 매김이 필요한 이유

Pagination은 클라이언트가 전송하고 렌더링하는 데이터의 양을 제한하고 스토리지/네트워크의로드를 줄이며 컬렉션을 "보행" 하는 결정 론적 방법을 설정합니다. 실제 시스템에서 페이지 매김은 '페이지 = 1 & 한계 = 50' 일뿐만 아니라 일련의 프로토콜 계약 및 일관성 불변입니다.

일반적인 목표:
  • 요청 당 대기 시간 및 메모리 제어.
  • 데이터 세트를 변경할 때 안정적인 탐색 (삽입/삭제).
  • 장소에서 재개 할 수있는 능력 (재개).
  • 캐싱 및 프리로드 (프리 페치).
  • 남용으로부터 보호 (속도 제한, 역압).

2) Pagination 모델

2. 1 OFFSET/LIMIT (페이지)

아이디어: "N 라인을 건너 뛰고 돌아 오십시오".
장점: 거의 모든 SQL/NoSQL과 호환되는 단순성.

단점:
  • 선형 저하: 큰 OFFSET은 전체 스캔/건너 뛰기 비용을 초래합니다.
  • 요청 사이의 삽입/삭제 중 불안정성 (오프셋 "플로트").
  • 정확한 "갱신 가능성" 을 보장하기는 어렵습니다.
SQL 예:
sql
SELECT
FROM orders
ORDER BY created_at DESC, id DESC
OFFSET 1000 LIMIT 50;

2. 커서/키셋/탐색 페이지화 2 개

아이디어: "K 키를 사용하십시오. "커서는 정렬 된 세트의 위치입니다.

장점:
  • O (1) 색인을 계속 사용할 수 있습니다.
  • 수집 중 안정성.
  • 깊은 "페이지" 에서 최고의 대기 시간.
단점:
  • 엄격하게 정의되고 독특하며 단조로운 정렬 키가 필요합니다.
  • 구현하고 디버깅하기가 더 어렵습니다.
SQL 예제 (찾기):
sql
-- Resumption after steam (created_at, id) = (:last_ts,:last_id)
SELECT
FROM orders
WHERE (created_at, id) < (:last_ts,:last_id)
ORDER BY created_at DESC, id DESC
LIMIT 50;

2. 3 연속 토큰

아이디어: 서버는 "위치" 가 인코딩 된 (및 파편/필터의 상태) 불투명 한 토큰을 반환합니다. 클라이언트는 내부를 이해하지 못하고 단순히 다음 페이지의 토큰을 반환합니다.
장점: 유연성, API를 깨지 않고 체계를 변경하는 기능.
단점: 토큰 수명 관리, 예금과의 호환성.

2. 4 시간 및 논리 커서

시간 기반: "모두 T까지 기록", 커서 - 타임 스탬프 (추가 전용 스레드에 적합).
로그 시퀀스/오프셋 기반: 커서-로그에서 오프셋 (Kafka offset, journal seq).
글로벌 단조로운 ID: 안정적인 탐색을위한 정렬 가능한 키로 Snowflake/UIDv7.

3) 코스 및 토큰 디자인

3. 좋은 커서 속성 1 개

Opaque-클라이언트는 형식에 독립적입니다.
저자/무결성: 스푸핑/조작을 방지하기위한 HMAC 서명.
상황: 정렬, 필터, 스키마 버전, 테넌트/샤드가 포함됩니다.

수명: 색인/액세스 권한을 변경할 때 TTL 및 "비 재생"

크기: DM에 적합한 컴팩트 (<= 1-2 KB).

3. 2 토큰 형식

권장 스택: JSON → 압축 (zstd/deplate) → Base64IM → HMAC.

페이로드 구조 (예):
json
{
"v": 3 ,//token version
"sort": ["created_at:desc","id:desc"],
"pos": {"created_at":"2025-10-30T12:34:56. 789Z","id":"987654321"},
"filters": {"user_id":"42","status":["paid","shipped"]},
"tenant": "eu-west-1",
"shards": [{"s ": "a, "" hi":"..."}] ,//optional: cross-shard context
"issued_at": "2025-10-31T14:00:00Z",
"ttl_sec": 3600
}

'mac = HMAC (비밀, 페이로드)' 가 상단에 추가되고 모든 것이 하나의 문자열 토큰으로 인코딩됩니다.

3. 3 안전

서명 (HMAC/Ś-256).
민감한 값 (PII) 이있는 경우 선택적으로 암호화 (AES-GCM).
서버 검증: 버전, TTL, 사용자 권한 (RBAC/ABAC).

4) 일관성과 불변

4. 1 안정적인 정렬

완전한 결정론을 사용하십시오: 'DESC, id DESC에 의한 주문'.
정렬 키는 고유해야합니다 ('id' 를 순위 결정자로 추가).

인덱스는 커버링 인덱스와 일치해야합니다

4. 스냅 샷 2 개 및 격리

점보가 아닌 페이지의 경우 읽기 일관된 스냅 샷 (MVCC/txid) 을 사용하십시오.
스냅 샷이 실용적이지 않으면 (비싼/많은 데이터) 계약을 체결하십시오. "커서는 위치 이전에 요소를 엄격하게 반환합니다. "이것은 뉴스 피드에 당연합니다.

4. 페이지 간 삽입/삭제 3 개

탐색 모델은 "복제/생략" 을 최소화합니다.
문서 삭제/수정 동작: 페이지 사이에 드문 "구멍" 이 허용되지만 "다시 시간" 은 허용되지 않습니다.

5) 인덱싱 및 ID 체계

복합 지수는 엄격하게 정렬 순서입니다: '(Pritten _ at DESC, id DESC)'.
모노톤 ID: Snowflake/UUIDv7은 시간 → 속도 향상 추구를 순서대로 제공합니다.
핫 키: 샤드 키 (예: '테넌트 _ id', '지역') 로 배포하고 파편 내부를 정렬하십시오.
ID 생성기: NTP 점프 중 충돌 및 "클럭 왜곡" - 시간 동기화, "회귀" 를 피하십시오.

6) 파편 간 페이지

6. 1 계획

Scatter-Gather: 모든 파편에 대한 병렬 요청, 현지 요청 과정, k-way는 애그리 게이터에서 병합됩니다.
파편 커서: 토큰에는 각 파편의 위치가 포함되어 있습니다.
바운드 팬 아웃 제한 단계당 파편 수 (속도 제한/타임 아웃 예산).

6. 멀티 샤드 용 토큰 2 개

저장 배열 '{shard _ id, last _ pos}'. 다음 단계에서 각 활성 파편에 대해 재개하고 다시 유지하여 전 세계적으로 정렬 된 페이지를 제공하십시오.

7) 의정서 계약

7. 1 REST

요청:

GET /v1/orders? limit=50&cursor=eyJ2IjoiMyIsInNvcnQiOiJjcmVh... (opaque)
답변:
json
{
"items": [ /... / ],
"page": {
"limit": 50,
"next_cursor": "eyJ2IjozLCJwb3MiOiJjcmVh...==",
"has_more": true
}
}
권장 사항:
  • 상한으로 '제한' (예: max = 200).
  • (PHP 3 = 3.0.6, PHP 4)
  • (PHP 3 = 3.0.6, PHP 4)

7. 2 GraphQL (릴레이 방식)

일반적인 '연결' 계약:
graphql type Query {
orders(first: Int, after: String, filter: OrderFilter): OrderConnection!
}

type OrderConnection {
edges: [OrderEdge!]!
pageInfo: PageInfo!
}

type OrderEdge {
node: Order!
cursor: String! // opaque
}

type PageInfo {
hasNextPage: Boolean!
endCursor: String
}

'커서' 는 불투명하고 서명되어야합니다. HMAC없이 "원시 Base64 (id)" 를 사용하지 마십시오.

7. 3 gRPC

(PHP 3 = 3.0.6, PHP 4)

proto message ListOrdersRequest {
string filter = 1;
int32 page_size = 2;
string page_token = 3; // opaque
}

message ListOrdersResponse {
repeated Order items = 1;
string next_page_token = 2; // opaque bool has_more = 3;
}

7. 스레드와 웹 소켓 4 개

연속 테이프의 경우: 커서는 "마지막으로 본 오프셋/ts" 입니다.

다시 연결하는 동안 '이력서' 를 지원합니다:
json
{ "action":"subscribe", "topic":"orders", "resume_from":"2025-10-31T12:00:00Z#987654321" }

8) 캐싱, 프리로드, CDNName

안정적인 필터가있는 첫 번째 페이지의 ETag/If-None-Match

공개 목록에 대해 짧은 TTL (예: 5-30 초) 의 캐시 제어.
프리 페치: '다음 _ 커서' 를 반환하고 힌트 ('Link: rel = "nether"') 를 반환하면 클라이언트가 다음 페이지를 미리로드 할 수 있습니다.
변형: 캐시 부분의 키로 '필터/정렬/로케일/테넌트' 를 고려하십시오.

9) 로드 관리 및 제한

상한선 '한계' (예: 200.
서버 측 압력: 요청 시간이> 예산 인 경우 응답에서 '제한' 을 줄이십시오 (클라이언트에 실제 페이지 크기를 명시 적으로 알려줍니다).
사용자/토큰/테넌트 당 요율 제한.
시간 초과/재시도: 지수 일시 정지, 반복 된 요청.

10) UX 측면

번호 매기기 방지: 무한 스크롤 → 커서; 숫자 페이지 → 오프셋 (데이터를 업데이트 할 때 부정확성을 설명하십시오).
장소로 돌아갑니다: 클라이언트 커서 스택을 저장합니다.
빈 페이지: '더 많은 _ more = 잘못된' 경우 더 많은 버튼을 표시하지 마십시오.
안정적인 경계: 정확한 '총' 을 저렴한 경우에만 표시하십시오 (그렇지 않으면 대략적인 '접근 _ 총').

11) 테스트 및 엣지 케이스

체크리스트:
  • 안정적인 정렬: 동일한 'ts' 를 가진 항목은 "깜박이지" 않습니다.
  • 삽입/삭제 - 복수는 페이지 교차점에 나타나지 않습니다.
  • 페이지 간 필터 변경: 토큰은 더 이상 사용되지 않거나 호환되지 않는 것으로 거부되어야합니다.
  • 토큰 TTL: 만료 후 유효한 오류.
  • 깊이: 대기 시간이 선형으로 자라지 않습니다.
  • 멀티 샤드: 올바른 병합 순서, 기아 "느린" 파편 없음.
속성 기반 테스트 예 (의사 코드):
python
Generate N entries with random inserts between calls
Verify that all pages are merged = = whole ordered fetch

12) 관찰 및 SLO

메트릭:
  • '리스트 _ 요청 _ latency _ ms' (P50/P95/P99) 페이지 길이.
  • (PHP 3 = 3.0.6, PHP 4)
  • (PHP 3 = 3.0.6, PHP 4)
  • 'merge _ fanout' (페이지 당 관련 파편 수).
  • (PHP 3 = 3.0.6, PHP 4)
로그/추적:
  • 로그에서 '커서 _ id' 와 관련이 있습니다. 마스크 페이로드.
  • 태그 범위: 'page _ size', 'source _ shards', 'db _ indx _ used'.
SLO 예:
  • 가용성: 99. '목록' 방법에서 9%.
  • 대기 시간: 로컬 요금으로 '페이지 _ 크기 <= 50' 에 대한 P95 <200 ms.
  • 토큰 오류: <0. 전체 통화 수의 1%.

13) 이주 및 상호 운용성

토큰에서 'v' 를 사용하고 이전 버전의 N 주를 지원합니다.
정렬 키를 변경할 때-커서없이 새로운 목록을 수행하라는 프롬프트와 함께 "소프트" 오류 '409 충돌' 을 보냅니다.
치명적인 경우 (모든 토큰의 포효): 'signing _ key _ id' 를 변경하고 오래된 경우를 거부하십시오.

14) 구현 예

14. 1 토큰 생성 (의사 코드)

python payload = json. dumps({...}). encode()
compressed = zlib. compress(payload)
mac = hmac_sha256(signing_key, compressed)
token = base64url_encode(mac + compressed)

14. 2 토큰 검증

python raw = base64url_decode(token)
mac, compressed = raw[:32], raw[32:]
assert mac == hmac_sha256(signing_key, compressed)
payload = json. loads(zlib. decompress(compressed))
assert now() - payload["issued_at"] < payload["ttl_sec"]
assert payload["filters"] == req. filters

14. 3 복합 키가있는 쿼리를 찾으십시오

sql
-- Page # 1
SELECT FROM feed
WHERE tenant_id =:t
ORDER BY ts DESC, id DESC
LIMIT:limit;

-- Next pages, continued after (ts0, id0)
SELECT FROM feed
WHERE tenant_id =:t
AND (ts <:ts0 OR (ts =:ts0 AND id <:id0))
ORDER BY ts DESC, id DESC
LIMIT:limit;

15) 안전 및 준수

PII를 도출 할 수있는 토큰에는 원필드를 포함하지 마십시오.
TTL에 서명하고 제한하십시오.
사용자간에 토큰을 견딜 수 없게 만드십시오 (페이로드에서 '하위/테넌트/역할' 을 작성하고 검증 중에 확인).
토큰 해시 만 기록하십시오.

16) 빈번한 오류 및 패턴 방지

커서로서의 Base64 (id): 가짜/픽업이 쉬우 며 종류를 변경할 때 계약을 위반합니다.
타이 브레이커 없음: 'id' → 중복/점프없이 'ORDER Łts DESC'.
토큰을 무효화하지 않고 페이지 간 필터를 변경하십시오.
깊은 관리: 느리고 예측할 수 없습니다.
버전이없는 토큰 및 TTL.

17) 미니 체크리스트 구현

1. 정렬을 정의하고 고유 한 타이 브레이커를 추가하십시오.
2. 이 순서에 대한 스패닝 인덱스를 만듭니다.

3. 모델을 선택하십시오: + 불투명 한 토큰을 찾으십시

4. 토큰 서명 (및 필요한 경우 암호화) 을 구현합니다.
5. TTL을 내려 놓고 버전을 정하십시오.
6. 포뮬레이션 및 문서 'has _ more', 'Next _ curder' 계약.
7. 크로스 샤드 체계 (필요한 경우) 및 k- 웨이 병합을 고려하십시오.
8. 메트릭, 알림 및 SLO를 추가하십시오.
9. 속성 기반 페이지 테두리를 테스트로 덮으십시오.

10. 토큰에 대한 마이그레이션 전략을 설명하십

18) 접근 방식 선택을위한 간단한 권장 사항

"페이지 번호" 와 대략적인 총계가 중요한 디렉토리/검색: 'OFFSET/LIMIT' + 캐시를 말합시다. 총계가 대략적이라고보고하십시오.
피드, 분석, 딥 리스트, 높은 RPS: 커서/찾기 만.
샤디/분산 컬렉션: 파편 당 커서 + 병합 토큰.
스레드/CDC: 이력서가있는 오프셋/t로 커서.

19) API 계약의 예 (요약)

'GET/v1/항목? 한계 = 50 & 커서 =... '

대답에는 항상 '페이지가 포함됩니다. ',' 페이지를 제한합니다. (PHP 3 = 3.0.6, PHP 4) 다음 _ 커서 '.
커서는 TTL로 불투명하고 부호가 있습니다.
정렬은 결정론적입니다. 'DESC, id DESC를 생성하십시오'.
변경 동작 항목을 설정하십시오-항목은 커서에 대해 "돌아 가지" 않습니다.

(PHP 3 = 3.0.6, PHP 4)

이 기사는 빅 데이터, 선명하고 적극적으로 변화하는 레코드 세트에서도 빠르고 예측 가능하며 보안을 유지하는 페이지 화를 설계하기위한 아키텍처 원칙과 기성품 패턴을 제공합니다.

Contact

문의하기

질문이나 지원이 필요하시면 언제든지 연락하십시오.우리는 항상 도울 준비가 되어 있습니다!

통합 시작

Email — 필수. Telegram 또는 WhatsApp — 선택 사항.

이름 선택 사항
Email 선택 사항
제목 선택 사항
메시지 선택 사항
Telegram 선택 사항
@
Telegram을 입력하시면 Email과 함께 Telegram에서도 답변드립니다.
WhatsApp 선택 사항
형식: +국가 코드 + 번호 (예: +82XXXXXXXXX).

버튼을 클릭하면 데이터 처리에 동의하는 것으로 간주됩니다.