03 / 指南
UUID vs ULID:為你的應用選擇識別碼
UUID v4 對 v7、ULID 的結構與可排序性、隨機識別碼對 B 樹索引的影響、碰撞機率、在 URL 中的暴露,以及如何在本機產生識別碼。
應用識別碼必須做到什麼
產生的識別碼只有一份本職工作——唯一——外加幾份順手接下的活:無需協調即可在任何地方產生、儲存緊湊、排序可預期、不洩露任何難堪的資訊。UUID 與 ULID 都過了唯一性這一關;分歧在那之後的一切。
- 無需協調去中心化產生:任何服務、工作執行緒或瀏覽器分頁都能鑄造識別碼,無需請示中央計數器,這正是兩種格式都適合分散式系統的原因。
- 同為 128 位元都是 128 位元:UUID 是寫成 36 個帶連字號十六進位字元的 128 位元值;ULID 把同樣的 128 位元裝進 26 個 base32 字元。
- 佈局才是關鍵真正的問題是佈局:哪些位元隨機、哪些位元編碼時間,決定了識別碼在索引裡的行為以及它會洩露什麼。
UUID 版本:v4 隨機,v7 依時間排序
RFC 9562 定義了幾個 UUID 版本,但應用開發裡只有兩個佔主導。第 4 版用隨機數填滿 128 位元中的 122 位元,不嵌入任何其他資訊;第 7 版以 48 位元 Unix 毫秒時間戳開頭,剩餘 74 位元填隨機數。
- v4:純隨機v4 是隱私最大化的選擇:值本身不透露產生的時間與地點,122 個隨機位元提供的唯一性任何應用都用不完。
- v7:時間在前v7 依建立時間排序,精確到毫秒,後產生的識別碼排在前者之後——這正是資料庫希望主鍵具備的性質。
- 跳過 v1舊版本屬於歷史:v1 嵌入 MAC 位址與時鐘值,兩者都會洩露;v3 與 v5 是基於名稱的雜湊,用於推導穩定 ID,不是新鮮隨機數。
ULID:文字形態也可排序且緊湊
一個 ULID 是 26 個 Crockford base32 字元:前 10 個字元編碼 48 位元毫秒時間戳,其餘 16 個編碼 80 個隨機位元。字母表刻意排除 I、L、O、U,因此識別碼經得起口頭朗讀和照截圖手敲。
- 字串排序即時間排序字典序即時間序:把 ULID 當普通字串排序,就依建立時間排好了,無需解析——在日誌行、檔名與鍵值儲存裡都好用。
- 26 個安全字元緊湊且 URL 安全:26 個不區分大小寫的字元,沒有連字號和符號,比 UUID 的 36 個字元短,放進任何路徑段或查詢參數都安全。
- 毫秒內單調同一毫秒內,次序來自隨機部分;像 Toolars 批次產生器那樣單調遞增它的產生器,連同毫秒的識別碼也能保持產生順序。
隨機 ID 會讓索引碎片化
B 樹索引是有序的,插入一個隨機 UUID v4 每次都會寫到隨機的葉子頁:頁分裂、緩衝池顛簸、索引被空洞撐大。依時間排序的識別碼追加在索引右緣附近,把插入變成 B 樹的最佳情形。
這就是為什麼這個選擇屬於綱要設計階段,而不是等第一個變慢的季度之後:遷移主鍵意味著重寫每一列以及引用它的每個外鍵。表會一直很小,v4 的無序永遠不會顯形;表會在持續寫入下長到數億列,有序格式從第一天起就在回報你。
- 隨機即離散用 v4 做主鍵時,寫入密集的表會出現更多頁分裂、更低的填充率,以及與同尺寸有序鍵相比明顯更差的寫入吞吐。
- 有序即追加v7 與 ULID 在保持去中心化產生的同時恢復局部性——沒有序列、沒有協調,也仍然無法預測其他列的 ID。
- 權衡要實測代價真實但有限:128 位元是 bigint 儲存的兩倍;依時間排序的鍵會把當天的寫入集中在索引右緣,這只在極高插入速率下才有影響。
碰撞、URL,以及 ID 會透露什麼
先算碰撞:v4 有 122 個隨機位元,產生十億個識別碼的碰撞機率約為 10 的 18 次方分之一——實際上永遠不會。ULID 的 80 個隨機位元守護的是另一份預算:同一毫秒內建立的識別碼。
把任何可疑的值貼進驗證器:它識別帶 RFC 9562 變體與版本半位元組的 36 字元標準 UUID 形態、帶溢位規則的 26 字元 ULID 字母表,並為依時間排序的格式報告嵌入的時間戳——日誌裡的一個值,在你基於它構建之前就能告訴你它是什麼。
- 機率可以忽略兩種格式都不是安全邊界:URL 裡的識別碼用於定址沒問題,但任何看到它的人都能引用它——授權必須來自存取檢查,而不是 ID 的不透明。
- ID 不是機密v7 與 ULID 按設計洩露建立時間;驗證器會公開地把它解析出來。這通常是無害的中繼資料,但對公開物件要有意識地做這個決定。
- 時間可見自增資料庫 ID 會洩露體量與成長速度;隨機和時間加隨機的識別碼不透露記錄數量的任何資訊。
不經過伺服器地產生與驗證
產生識別碼只需要一種稀缺資源——好的隨機性——而瀏覽器本來就有。工作區從 crypto.getRandomValues 取隨機數,整個批次都在分頁內產生,從不傳輸任何值。
- 每批 1–100 個選擇 UUID v4、UUID v7 或 ULID,每批產生 1 到 100 個;v7 與 ULID 批次會單調遞增隨機欄位,整批都保持產生順序。
- 大小寫與匯出輸出大小寫只是顯示選擇:標準形、大寫或小寫,支援一鍵複製全部,以及換行文字與 CSV 下載,CSV 會記錄每個識別碼的嵌入時間。
- 即時驗證驗證即時回答結構問題:長度、字母表、UUID 版本與變體、nil 與 max 特殊值、ULID 溢位,以及 UTC 的嵌入時間戳。
ULID 是 base32;你的權杖多半是 Base64。
位元組到文字的編碼藏在每個識別碼與權杖之下——弄清它們的代價,以及在哪裡值得用。