GH GambleHub

分割和遊標

1)為什麼需要分區

分割限制了客戶端傳輸和渲染的數據量,減輕了存儲/網絡的負載,並指定了確定性「走遍」集合的方式。在現實世界中,分割不僅是「page=1&limit=50」,而且是一組協議合同和一致性不變量。

示範目標:
  • 控制請求的潛伏期和內存。
  • 更改數據集(插入/刪除)時保持導航穩定。
  • 從現場恢復的能力。
  • 腰帶和預裝(預裝)。
  • 防止濫用(rate限制,backpressure)。

2)分割模型

2.1 OFFSET/LIMIT(頁面)

想法:「跳過N行,返回M」。
優點:簡單,幾乎與任何SQL/NoSQL兼容。

缺點:
  • 線性降解:大OFFSET 導致完全掃描/skip-cost。
  • 在查詢之間插入/刪除時不穩定性(偏移「浮動」)。
  • 很難提供準確的「可再生性」。
SQL示例:
sql
SELECT
FROM orders
ORDER BY created_at DESC, id DESC
OFFSET 1000 LIMIT 50;

2.2 Cursor/Keyset/Seek分離

想法:「繼續使用K鍵。」遊標是排序集中的位置。

優點:
  • O (1)在索引存在時繼續訪問。
  • 集合更改時的穩定性。
  • 深層頁面上的最佳潛伏期。
缺點:
  • 需要嚴格定義、唯一和單調的排序鍵。
  • 更難實現和調試。
SQL示例(seek):
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 Continuation tokens(不透明令牌)

想法:服務器返回一個opaque令牌,其中「位置」(以及可能的shards/過濾器狀態)被編碼。客戶無法理解內臟,只需返回下一頁的令牌即可。
優點:靈活性,能夠在不打破API的情況下更改電路。

缺點: 代幣壽命管理,代幣兼容性.

2.4時間和邏輯遊標

基於時間:「所有條目均為T」,光標為時間標簽(僅適用於上下文流)。
Log-sequence/offset:光標-log中的偏移(Kafka offset, journal seq)。
Global monotonic IDs: Snowflake/UUIDv7作為穩定搜索的可分類密鑰。

3)課程和令牌設計

3.1好光標屬性

不透明度(opaque):客戶端與格式無關。
作者/完整性:HMAC簽名以防止篡改/操縱。
上下文:包括排序,過濾器,模式版本,tenant/shard。
使用壽命:更改索引/訪問權限時的TTL和「無效」(非重復)。
尺寸:緊湊型(<=1-2 KB),適合URL。

3.2令牌格式

推薦堆棧:JSON →壓縮(zstd/deflate)→ Base64URL → 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(秘密,payload)」,並且全部編碼為單個字符串令牌。

3.3安全性

簽字(HMAC/SHA-256)。
在存在敏感值(PII)時可選地加密(AES-GCM)。
服務器驗證:版本、TTL、用戶權限(RBAC/ABAC)。

4)一致性和不變性

4.1穩定排序

使用完整的確定性:「ORDER BY ts DESC, id DESC」。
排序鍵必須是唯一的(將「id」添加為tiebreaker)。
索引必須與排序(覆蓋索引)匹配。

4.2快照(snapshot)和隔離

對於「不穩定」頁面,請使用read-consistent snapshot (MVCC/txid)。
如果snapshot不切實際(昂貴/大量數據),則制定合同:「光標返回項目,嚴格先於位置。」新聞錄像帶很自然。

4.3頁面之間的插入/刪除

Seek模型最小化「重復/跳過」。
記錄刪除/更改時的行為:允許頁面之間出現罕見的「漏洞」,但不允許時間倒退。

5)索引和ID模式

復合索引嚴格按排序順序排列:「(created_at DESC,id DESC)」。
單調ID:Snowflake/UUIDv7給出時間順序→加速seek。
熱鍵:分布在shard-key(例如'tenant_id'、'region'等)上,然後在shard-key內排序。
ID生成器:避免沖突和「未來時鐘」(clock skew)-時間同步,NTP跳躍中的「回歸」。

6)Cross Shard Pagination

6.1個方案

Scatter-Gather:對所有沙丁魚的並行查詢,本地seek課程,然後是聚合器上的k-way merge。
Per-Shard Cursors:令牌包含每個令牌的位置。
受限制的禁區:一步限制硬幣的數量(等級限制/定時預算)。

6.2多重硬幣代幣

存儲數組'{shard_id, last_pos}'。在接下來的步驟中,為每個活動的shard復活,然後再次閃爍,給出一個全球排序的頁面。

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)。
  • 如果「has_more=false」,則不存在「next_cursor」。
  • GET的冪等性,非「next_cursor」響應的可緩存(固定過濾器和快照的頭版)。

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
}

「cursor」必須不透明且簽名;不使用HMAC的「原始Base64 (id)」。

7.3 gRPC

使用「page_size」和「page_token」:
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線程和WebSockets

對於連續磁帶:遊標為「最後看到的offset/ts」。

重構時支持「resume_from」:
json
{ "action":"subscribe", "topic":"orders", "resume_from":"2025-10-31T12:00:00Z#987654321" }

8) Keshing,預裝,CDN

ETag/If-None-Match用於具有穩定過濾器的首頁。
具有短TTL的Cache-Control(例如5-30 s)用於公共列表。
Prefetch:返回"next_cursor"和提示("Link: rel="next"),客戶端可以預覽下一頁。
變體:將「過濾器/sort/locale/tenant」作為腰果的鑰匙。

9)負載管理和限制

「極限」的上限,例如200。
Server side backpressure:如果查詢時間>預算,請減少響應中的「限制」(並明確告知客戶端實際頁面大小)。
每個用戶/令牌/tenant的價格限制。
Timeout/Retry:指數暫停、偶數重復查詢。

10) UX方面

滾動與編號:無限滾動→光標;數字頁面→ offset(但在更新數據時解釋不準確)。
「返回到位」按鈕:存儲客戶端遊標堆棧。
空白頁面:如果「has_more=false」,請勿顯示「更多」按鈕。
穩定的界限:僅在價格便宜時才顯示準確的「總」(否則為近似的「approx_total」)。

11)測試和邊緣案例

支票單:
  • 穩定排序:具有相同'ts'的元素不會「閃爍」。
  • 插入/刪除:頁面交界處不會出現重復。
  • 更改頁面之間的過濾器:令牌必須被拒絕為「過時/不兼容」。
  • 令牌TTL:到期時正確錯誤。
  • 深度:潛伏期沒有線性增長。
  • Multichard:正確的merge順序,沒有「慢速」的硬幣。
基於屬性的測試示例(偽代碼):
python
Generate N entries with random inserts between calls
Verify that all pages are merged = = whole ordered fetch

12)可觀察性和SLO

度量標準:
  • 「list_request_latency_ms」 (P50/P95/P99)按頁面長度排列。
  • 「seek_index_hit_ratio」(在覆蓋索引上離開的查詢比例)。
  • 「next_cursor_invalid_rate」 (驗證錯誤/TTL/簽名)。
  • 「merge_fanout」(每頁涉及的shards數量)。
  • 「duplicates_on_boundary」和「gaps_on_boundary」(客戶端遙測中的細節)。
Logi/Tracing:
  • 在日誌中關聯「cursor_id」,掩蓋付費。
  • 標記「page_size」、「source_shards」、「db_index_used」。
SLO示例:
  • 可用性:99。9%的「列表」方法。
  • 潛伏期:P95 <200 ms for 'page_size<=50'在本地魅力下。
  • 令牌錯誤:<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個帶有復合密鑰的Seek查詢

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。
嘗試使令牌在用戶之間不耐受(在付費下載中刻上「sub/tenant/roles」,並在驗證時進行鉆探)。
僅編寫令牌哈希。

16)頻繁的錯誤和反模式

Base64 (id)作為光標:易於偽造/選擇,在更改排序時打破合同。
缺席決勝局:「ORDER BY ts DESC」沒有「id」 →重復/跳躍。
在頁面之間更改過濾器而不會破壞令牌。
Deep OFFSET:緩慢而不可預測。
沒有版本和TTL的令牌。

17)實施迷你支票

1.定義排序並添加唯一的tie-breaker。
2.按照此順序創建覆蓋索引。
3.選擇模型:seek+不透明令牌。
4.實現令牌簽名(並在必要時加密)。
5.放置TTL和轉換。
6.建立和記錄合同「has_more」、「next_cursor」。
7.想想一個交叉沙丁魚方案(如果需要)和k-way merge。
8.添加度量標準、Alerta和SLO。
9.覆蓋基於屬性的頁面邊界測試。
10.描述令牌的遷移策略。

18)關於選擇方法的簡要建議

「頁碼」和近似總數很重要的目錄/搜索:假設「OFFSET/LIMIT」+緩存;報告總數是近似值。
磁帶,分析師,深層列表,高RPS:僅cursor/seek。
硬質/分布式集合:按硬質cursors+merge令牌。
流/CDC:遊標為offset/ts並恢復。

19) API約定示例(摘要)

`GET /v1/items?limit=50&cursor=...`

答案總是包括'page。limit`, `page.has_more',可選的「頁面」。next_cursor`.

光標是不透明的,簽名,帶有TTL。
排序是確定性的:「ORDER BY created_at DESC,id DESC」。
更改集時的行為:項目不相對於遊標「返回」。
度量和錯誤是標準化的:「invalid_cursor」,「expired_cursor」,「mismatch_filters」。

本文給出了結構原理和現成的圖案來設計分區,即使在大數據,硬化和積極變化的記錄集環境中,分區仍保持快速,可預測和安全。

Contact

與我們聯繫

如有任何問題或支援需求,歡迎隨時聯絡我們。我們隨時樂意提供協助!

開始整合

Email 為 必填。Telegram 或 WhatsApp 為 選填

您的姓名 選填
Email 選填
主旨 選填
訊息內容 選填
Telegram 選填
@
若您填寫 Telegram,我們將在 Email 之外,同步於 Telegram 回覆您。
WhatsApp 選填
格式:國碼 + 電話號碼(例如:+886XXXXXXXXX)。

按下此按鈕即表示您同意我們處理您的資料。