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,我们也会在 Telegram 回复您。
WhatsApp 可选
格式:+国家代码 + 号码(例如:+86XXXXXXXXX)。

点击按钮即表示您同意数据处理。