ULID 生成器

生成可排序的 26 位 ULID(Crockford base32)並可將內嵌時間戳解碼回日期,非常適合作為資料庫主鍵。

可排序的唯一標識。 ULID 為 26 位字元:前 10 位以毫秒編碼建立時間,後 16 位為隨機值。因此它可按字典序排序,也能安全用作主鍵。在瀏覽器內生成。

ULID 與 UUID 的取捨

ULID 是什麼

ULID 是一個 128 位識別符號,寬度與 UUID 相同,分為兩部分:48 位毫秒時間戳,後接 80 位隨機資料。它以 26 個字元的 Crockford base32 表示,而不是 UUID 那種帶連字元的 36 字元十六進位制,因此更短、大小寫不敏感,並且剔除了最容易看錯的字元——字母表中不含 I、L、O 和 U。

真正關鍵的差異不在編碼,而在排序。由於時間戳佔據高位,而 base32 保持位元組序,把 ULID 當作普通字串排序,結果就是按建立時間排序。同一毫秒內生成的兩個 ID 會退化為比較隨機部分,但跨毫秒的順序是精確的。

可排序性對資料庫為什麼重要

UUIDv4 是均勻隨機的,而這恰恰是聚簇索引最不需要的性質。每次插入都落在 B 樹的隨機位置,資料庫因此不斷分裂頁面並觸碰冷頁。索引膨脹到超出必要的體積,寫入吞吐也隨表增長而劣化。這是 MySQL InnoDB 和 SQL Server 中 UUID 主鍵眾所周知的問題。

按時間有序的識別符號會追加在索引的右端附近,訪問模式與自增整數一致。你既保留了客戶端生成全域性唯一鍵的好處——無需往返取 ID、服務之間無需協調、跨分片合併安全——又避開了索引碎片化。

什麼時候該換別的方案

RFC 9562 標準化的 UUIDv7 用標準 UUID 的格式和佈局實現了與 ULID 相同的效果。如果你的技術棧已經具備原生 UUID 列型別和配套工具,如今通常應優先選 UUIDv7,理由很簡單:它能直接融入既有生態。而當你希望在 URL 或日誌中使用更短、更友好的文本形式時,ULID 依然有吸引力。

無論選哪種,都要清楚時間戳對任何持有該 ID 的人都是可見的。建立時間會洩露,相鄰 ID 會暴露先後順序——凡是列舉關係或時間資訊敏感的場景都不要使用。另外,每毫秒 80 位隨機數意味著碰撞在實踐中不必擔心,但 ULID 並不是秘密,絕不能當作訪問憑證使用。

開源說明:使用 crypto.getRandomValues 與自研 Crockford base32 編解碼實現,不依賴第三方庫。

常見問題

ULID 和 UUID 有什麼區別?
兩者都是 128 位,但 ULID 把 48 位毫秒時間戳放在最前面,因此按字串排序就等於按建立時間排序。UUIDv4 完全隨機,排序結果沒有意義。
可排序對資料庫為什麼重要?
隨機主鍵會把插入操作打散到 B 樹各處並造成索引碎片。時間有序的主鍵總是追加在尾部附近,寫入保持順序,熱頁數量也小得多。
同一毫秒內生成的 ID 還能保持有序嗎?
在單調模式下可以。工具不會重新抽取隨機部分,而是對其遞增,因此同一毫秒內生成的多個 ID 依然按順序排列。
ULID 可以公開暴露嗎?
隨機部分無法猜測,但時間戳任何人都能讀出來。如果記錄的建立時間屬於敏感資訊,就不要用 ULID 作為它的公開識別符號。
應該選 ULID 還是 UUIDv7?
兩者解決的是同一個問題。UUIDv7 已在 RFC 9562 中標準化,能直接沿用現有的 UUID 欄位和類庫,新專案優先選它,除非你確實需要 26 字元的 Crockford 形式。
為什麼是 26 個字元?
128 位用 Crockford Base32 編碼正好需要 26 個符號。該字母表剔除了 I、L、O、U,既避免轉錄錯誤,也避免拼出意外的單詞。