GH GambleHub

生成ID

1)為什麼要註意標識符

ID (ID)是實體的基本密鑰:DB行、消息、文件、順序。以下內容取決於其屬性:
  • 唯一性和規模(沖突,水平增長)。
  • 順序和排序(時間相關性,復制,去除)。
  • 存儲性能(索引、熱頁、密鑰大小)。
  • 安全(不可預測,泄漏,猜測)。
  • 可用/集成(短的URL安全,對大小寫不敏感)。

ID選擇是熵、有序性、長度、生成速度和操作之間的權衡。

2)關鍵要求和術語

獨特:沖突的可能性必須低於可接受的風險。
熵:「多少隨機性」包含ID(位)。
可排序性(time-sortable/k-sortable):詞典排序≈時間排序。
單調:節點/流中不可減少的序列。
記錄位置:新插入集中於索引的「尾部」(熱頁危險)的程度。
可預測性:是否可以猜測相鄰ID (安全性/API重要)。
表示:二進制/字符串,Base16/32/36/58/64,連字符,寄存器。

3)主要標識符家族

3.1 UUID

v4 (random): 122位熵。無序,有利於安全和簡單。減:由於隨機分配,索引會「混亂」-但是,這會均勻地分散負載並刪除「熱頁」。
v1 (time+MAC):排序,但攜帶IAU/時間(隱私);經常避免。
v7(時間順序):毫秒時間+隨機部分。按時間對詞典進行排序,並在DB中進行良好的壓縮。折衷方案:出現索引的「熱尾巴」;通過硬化/前綴/增量處理。

提示

對於外部API和非嚴格順序要求-v4。
對於事件/邏輯DB和「可排序」密鑰為v7。

3.2 ULID (Crockford Base32)

128位:48位時間(ms)+80位隨機性。按時間進行詞典排序,工人友好(沒有「I,L,O,U」),URL安全。有一個單調變化(在相同的時間標記下,隨機部分增加)。
優點:可讀性,有序性,可移植性。
缺點:在非常高的時間點插入頻率是「熱尾巴」。

3.3 KSUID

160位:32位時間(秒)相對於時代+128位隨機性。更大的時間範圍和穩定的排序,字符串比ULID?(不-更長,但有編碼),對分布式日誌和對象有好處。

3.4類似雪花(k-sortable flake ID)

經典方案(可自定義):

[ timestamp bits ][ region/datacenter bits ][ worker bits ][ sequence bits ]

屬性:節點上的單調生長,準全局唯一性,短(64位)二進制表示。
風險:時鐘依賴性(漂移/時間倒退),在單個柚木中耗盡序列,協調區域/工作器位。
治療:對「clock back」的保護,序列備份,時間檢測器,PTP/NTP紀律。

3.5個數據庫序列(SEQUENCE/IDENTITY)

單個DBMS/Chard中最簡單的單調生成。
優點:簡短、快速、方便本地表格。
缺點:在分布式集群中很難在全球範圍內使用;可以預見(不安全地作為公共鑰匙),會產生索引的熱尾巴。

3.6內容地址ID (hash content)

內容SHA-256/Blake3 →穩定的ID、重復數據消除、完整性檢查、緩存。
優點:確定性,替代保護。
缺點:昂貴的生成(CPU),碰撞實際零,沒有時間排序,長度。

4)沖突和「生日悖論」(直覺)

「n」生成下隨機ID 「b」位發生沖突的概率是近似的:

p ≈ 1 - exp (-n (n-1 )/2/2 ^ b) ≈ n ^ 2/2 ^ (b + 1) (for small p)
示例:
  • UUIDv4 (122位)=10^12(萬億)→ p ~ 1e-14(可忽略)。
  • 64位蘭德→ n=10^9已經是p ~ 0。027(明顯風險)。
  • 結論:對於大型系統,64位隨機系統通常很少。使用96/128位。

5)索引、熱頁和存儲

隨機密鑰(v4)在索引樹上均勻分布插入物→沒有「尾巴」,但比緩存局部性差。
按時間排序(v7/ULID/Snowflake)將「插入尾巴」→更好的位置和壓縮,但在高並行記錄下存在熱頁的風險。

軟化「熱尾巴」:
  • tenant/region 前綴/sharding(在時間之前添加1-2字節);
  • 間歇性:老年人的部分意外;
  • Butch插件,B樹中的濾鏡,BRIN/大型日誌聚類上的自動轉換。
大小很重要:
  • 「UUID(16B)」vs 「BIGINT(8B )」/「INT8」節省了內存/緩存;Base32/58/64行增加了20-60%。對於DB,存儲二進制,序列化到邊緣的一行。

6)安全和隱私

未使用SEQUENCE/INT作為URL/API中的公共ID:可猜測→資源枚舉。
為外部鏈接添加隨機、不可預測的ID (v4/v7/ULID/KSUID)。
不要在ID中編碼PII。如果要啟用屬性-加密/簽名(例如JWE/JWS)或使用不透明令牌。
URL安全編碼:Base32 Crockford, Base58(沒有「0OIl」), Base64url。

7)多重性、前綴和路由

格式:「[TENANT_PREFIX]-[ID]」或二進制:'tenant_id || id'。
優點:快速過濾器/按租戶分批,N+1掃描保護。
缺點:可能會降低舊位中的熵密度→考慮分布(前綴哈希)。
Hash後綴(2-3字節)可以減少沖突並幫助共享路由:'shard=hash(id)%N'。

8)實用選擇建議

API,公共鏈接,無嚴格順序的分布式服務:UUIDv4,ULID/KSUID。
經常按時間排序的邏輯/事件/訂單:UUIDv7或ULID(單調)。
具有本地單調性和短鍵的超高帶寬:類似於Snowflake的64位(需要時間紀律)。
文物庫/法案/斑點:內容地址(SHA-256),以及人性化的簡短「展示櫃」(Hashids/鏈接)。
單個DB中的本地表:SEQUENCE/IDENTITY+用於公共參考(掩碼)的外部「包裝」。

9)實現和示例

9.1 PostgreSQL

按需要將UUID二進制存儲,索引為「btree」或「hash」。

sql
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE orders (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- или uuid_generate_v4()
created_at timestamptz NOT NULL DEFAULT now(),
tenant smallint NOT NULL
);

-- For time-sortable (UUIDv7) store binary (uuid), generation in the application.
-- If you want a cluster by time:
CREATE INDEX ON orders (created_at DESC);
Sequential hot fix:對於time-sorted ID,在高級位中添加「鹽」或tenant派對:
sql
CREATE TABLE orders_t1 PARTITION OF orders FOR VALUES IN (1);
CREATE TABLE orders_t2 PARTITION OF orders FOR VALUES IN (2);

9.2 Redis(原子計數器/單子子)

bash
INCR "seq: orders" # local sequence combine: epoch_ms<<20     (worker_id<<10)      (seq & 1023)

9.3個類似雪花的生成器(偽代碼)

pseudo const EPOCH =  1704067200000  # custom epoch (ms)
state: last_ms=0, seq=0, worker=7, region=3

next():
now = epoch_ms()
if now < last_ms: wait_until(last_ms)    # защита от clock back if now == last_ms:
seq = (seq + 1) & ((1<<12)-1)      # 12 бит if seq == 0: wait_next_ms()
else:
seq = 0 last_ms = now return (now-EPOCH)<<22      region<<17      worker<<12      seq

9.4 ULID/UUID在應用中

Go

go
// ULID t:= time. Now(). UTC()
entropy:= ulid. Monotonic(rand. New(rand. NewSource(t. UnixNano())), 0)
id:= ulid. MustNew(ulid. Timestamp(t), entropy)

//UUID v7 (if there is a library)
id:= uuid. Must(uuid. NewV7())

Node.js

js import { ulid } from 'ulid';
import { v4 as uuidv4 } from 'uuid';
const id1 = ulid();
const id2 = uuidv4(); // v4

Python

python import uuid, time id_v4 = uuid. uuid4()
For v7, use a library (for example, uuid6/7 third-party packages)

10)編碼和表示

DB中的Binarno(「BYTEA」,「UUID」)→緊湊而快速。在邊緣,轉換為:
  • Base32 Crockford (ULID):對大小寫不敏感,沒有視覺上相似的字符。
  • Base58:簡稱Base32/64人為令牌,URL安全。
  • Base64url:簡而言之,但URL中的「-」和「_」。

穩定大小寫和格式(連字符/連字符不存在),以便在比較行時避免重復。

11)測試花花公子和可觀察性

沖突:度量'id_collision_total'(必須為0), alert在>0時。
前綴分布:高級字節直方圖-尋找占位符。
生成率:「ids_per_sec」,p99生成器潛伏期。
Clock skew(用於Snowflake):節點越位,「clock went back」事件。
索引尾巴:p95/p99 「INSERT」後綴;鎖定/熱頁的比例。

Game day:

註入「clock drift/back」 →確保生成器在等待/切換。
在毫秒內溢出「序列」→檢查等待next_ms。
大規模並發→索引中沒有鎖定風暴。

12)反模式

AUTO_INCREMENT/SEQUENCE如公共ID:猜測,泄漏。在內部使用公共不透明ID。
UUIDv1(IAU/時間)向外:隱私。
每萬億條記錄64 位隨機ID:真正的沖突風險。
沒有HA的全局「中央發電機」:SPOF和瓶頸。
不帶時鐘保護的Time-sorted ID:重復/反向順序。
混合不同的ID格式而不使用顯式版本/前綴,→混亂/遷移。
將ID保存為具有不同寄存器/形狀的字符串→隱藏的副本。

13)實施支票

  • 為域要求選擇格式(v4/v7/ULID/KSUID/Snowflake/SEQ/hash)。
  • 定義了順序要求(是否需要排序)。
  • 評估了碰撞概率(b位,n生成)並設置了風險閾值。
  • 設計了編碼(在DB+人造展示中二進制)。
  • 對於計時的-反時鐘保護、序列限制和NTP/PTP紀律。
  • 對於公共ID-不可預測性(random/ULID/KSUID),缺少PII。
  • 深思熟慮shard routing (hash (id)% N),多影子前綴。
  • 可觀察性:沖突,分布,延遲,clock skew的度量。
  • 排序溢出/高競爭/窗口長度的測試案例。
  • 格式、版本、時代、位標記和遷移計劃的文檔。

14) FAQ

Q: 為微服務選擇「默認」是什麼?

A:UUIDv7或ULID:時間順序,大熵和邊緣上的簡單生成。對於外部API,也是c ULID/UUIDv4。

Q:需要簡短而人性化的ID。
A: ULID/KSUID或Base58編碼128位隨機/時間ID。記住長度和沖突。

問:可以做「短數字」ID,但安全嗎?
答:是的:儲存內部SEQ,向外贈送蛋白質令牌(random 96-128位)或帶有鹽+簽名的Hashids。

問:如何從SEQ遷移到UUIDv7?
A:鍵入新列「id_new」 (UUID)、二進制、發布指向新ID的鏈接、然後切換RK/外鍵並刪除舊鍵。

Q:為什麼我的ULID插件變得「熱」?
答:在單個索引中插入嚴格增加的鍵。按批次/tenant粉碎,攪拌高級位,使用擊球插入。

15)結果

良好的ID是任務下的正確屬性集:足夠的熵、可預測的排序(如果需要)、安全的宣傳和健康的索引利用。選擇簡單和分布的UUIDv4/ULID/UUIDv7/KSUID,Snowflake選擇密集的單調和短鍵(在時間學科中),序列選擇本地表,內容哈希選擇人工制品。設置可觀察性和測試-ID將不再是驚喜的來源。

Contact

與我們聯繫

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

Telegram
@Gamble_GC
開始整合

Email 為 必填。Telegram 或 WhatsApp 為 選填

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

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