跳到主要內容
楓葉工具箱 ToolMaple

開發最後更新:

UUID 產生器

產 UUID v4 或 v7,要幾個給幾個,一鍵複製。

產生 0 個

    UUID 是什麼?為什麼流水號 1、2、3 會不夠用

    UUID(Universally Unique Identifier,通用唯一識別碼)是 128-bit 的識別碼,用 32 個 16 進位字元加 4 個連字號表示成 8-4-4-4-12 格式,例如 550e8400-e29b-41d4-a716-446655440000。原始規範 1997 年由 OSF DCE 提出,IETF RFC 4122 標準化。2024 年新版 RFC 9562 把 v6 / v7 / v8 納入官方規範。

    128-bit 的空間是 2^128 ≈ 3.4 × 10^38。v4 有 122 個隨機位元,要產到約 2^61 個(2.3 × 10^18)才有一半機率撞到一次,即使每秒產生 10 億個也要 73 年。所以不必中央伺服器發號,各個分散式節點本地產生也不會撞 ID。

    本頁用瀏覽器內建的 crypto.randomUUID() 與 crypto.getRandomValues() 產生,兩者都是為密碼學用途設計的 API。

    跟流水號(auto-increment)比:

    • Auto-increment(1, 2, 3...):小整數、儲存省、索引極快、人類好讀。劣:洩漏「我們有幾筆資料」、跨服務難整合、必須中央發號
    • UUID:分散式產生、不洩漏總數、跨系統整合容易。劣:佔 16 bytes(vs 整數的 4-8),v4 碰索引可能 fragment(v7 解決)
    • 實務:對外的 public ID 用 UUID v7,內部報表 join 鍵用整數,可兼顧兩邊

    流水號還有一個常被忽略的問題:它是公開的算術。客戶看到 /invoices/1042 就知道你大概開了幾張發票,順手把數字減一還能試試看別人的那張。

    版本怎麼挑、產出來要用在哪

    本頁只產 v4 與 v7 兩種,因為新專案值得考慮的就這兩個;其餘版本列在這裡,是為了讓你在別人的資料裡認得出來。認不出來的,貼進工具下方的「辨識 UUID」欄,會告訴你是哪一版、哪種變體,v1、v6、v7 還會解出建立時間。

    • v1:時間 + MAC 位址。會洩漏產生主機的網卡資訊,現代少用
    • v3 / v5:用名稱 + namespace 算 hash(v3 MD5、v5 SHA-1)。相同輸入永遠相同 UUID,適合「以網址產生穩定 ID」
    • v4:純亂數。最常見、相容性最好
    • v6 / v7:時間排序版。前 48 bit 是時間戳,可被 B-tree 索引高效排序
    • v8:自訂版。RFC 9562 預留給「廠商自己定義內容」

    v4 還是 v7?

    • v4(隨機):完全亂數,無時間順序。最常見、相容性最好(所有資料庫都支援)。 不需排序時用這個。
    • v7(時間排序):前 48 bit 是 Unix ms 時間戳。做為資料庫主鍵時索引效能最佳(插入順序與儲存順序一致,不會像 v4 那樣造成 B-tree split)。RFC 9562 標準,後端建議首選。

    常見使用情境:

    • 資料庫主鍵(PostgreSQL UUID 型別、MongoDB ObjectId 替代)
    • API request ID / 分散式追蹤 correlation ID
    • session token、CSRF token(短期用)

    另外兩個不是拿來發的值:全 0 的 Nil 與全 f 的 Max 是規範保留的哨兵值,看到它們通常代表某段程式忘了填。

    怎麼一次產一批:測試資料與 migration

    1. 先選版本。這批 ID 會進資料表、而且你會照建檔順序查,就選 v7;其他情況 v4 就夠。
    2. 把數量(最多 1000)填成你要的筆數。欄位收 1 到 1000,數字一改清單就跟著重產。
    3. 按重新產生換一批相同設定的新值。本頁不記任何東西,重新整理就是另一批。
    4. 按全部複製。複製出來是一行一個,試算表欄位、SQL 的 VALUES 清單與多數 CSV 匯入都吃這個格式;每一列旁邊的複製鈕則只複製那一個。
    5. 關掉分頁前先貼到存得住的地方,這裡不留紀錄,關掉就找不回來。

    測試的 fixture 建議把值寫死在程式碼裡,不要在測試執行時才呼叫產生器。 測試失敗時,你才能直接拿那串常數去全專案搜尋。

    怎麼把 v7 當主鍵而不會後悔

    1. 欄位型別優先用 16 個位元組的原生型別:PostgreSQL 的 uuid、SQL Server 的 uniqueidentifier、MySQL 的 BINARY(16)、SQLite 的 BLOB 或 TEXT。
    2. 資料庫沒有 v7 函式時就在應用層產。插入前就知道 ID 的好處是,你可以在記憶體裡把整組關聯物件組好,再用一個 transaction 寫進去。
    3. 建檔時間敏感的資料別把 v7 放進對外網址。v7 等於公告這筆資料何時建立,連續幾筆還會洩漏你的建檔速度。
    4. 另外給人看的要有短編號,例如發票號碼或訂單編號。沒有人想在電話裡念 36 個字元。
    5. 索引要克制。每一個次要索引都會複製一份 16 位元組的主鍵,開六個索引就付六次。

    把產好的 ID 帶到要用的地方

    全部複製是用換行接起來的,所以貼到哪裡就決定你拿到什麼。

    • 試算表:Excel、Numbers 與 Google 試算表收到換行分隔的貼上,會一列一個值填好。
    • 程式碼與 SQL:貼進編輯器後用多游標一次把每行加上引號跟逗號。VS Code 系的編輯器按 Ctrl+Alt+↓ 或 Cmd+Option+↓ 就能在每一行放一個游標。
    • 終端機:多行貼上在 shell 裡可能直接被當指令執行,不確定剪貼簿裡是什麼就先過一次編輯器。

    各語言與資料庫怎麼產、怎麼存

    每個生態系都內建 v4,v7 則是後來才有、支援程度不一。 下面是撰寫當下的情況,實際請以你用的版本的官方手冊為準。

    • JavaScript、TypeScript:crypto.randomUUID() 在瀏覽器與 Node 都回 v4,但它要求 secure context,所以在純 HTTP 的頁面上會是 undefined。v7 沒有內建,要用 uuid 這類套件,或像本頁一樣自己用 crypto.getRandomValues() 組。
    • PostgreSQL:有原生 uuid 型別,存 16 個位元組;gen_random_uuid() 從 13 版起內建,回傳 v4。較新的版本另外提供 uuidv4() 與 uuidv7(),沒有的話就在應用層產 v7。
    • MySQL、MariaDB:沒有 UUID 欄位型別,而且 UUID() 回的是不照時間排序的 v1。慣例是用 UUID_TO_BIN(uuid, 1) 存進 BINARY(16),第二個參數會把時間欄位對調,存下來的位元組才排得動,讀出來用 BIN_TO_UUID(id, 1)。兩邊的旗標要一致,不然值會錯位。
    • SQLite、SQL Server:SQLite 既沒有型別也沒有產生器,存文字或 16 位元組的 BLOB,值在應用層產。SQL Server 有 uniqueidentifier,NEWID() 給隨機值、NEWSEQUENTIALID() 在同一個 session 內遞增。
    • Python、Java、Go:uuid.uuid4() 與 UUID.randomUUID() 都在標準函式庫裡,Go 慣例是用 github.com/google/uuid,它也有 v7。時間排序版進 Python 的 uuid 模組是很近期的事,JDK 的 UUID 類別也還沒有 v7 工廠方法,用之前先確認版本。
    • .NET:Guid.NewGuid() 是 v4,較新版本 另外有 v7 工廠方法。要注意位元組順序:Guid.ToByteArray() 的前三段是小端序,跟別的語言共用同一個 BINARY(16) 欄位時,會安靜地產生對不起來的識別碼。

    另一個坑是比對。UUID 之間的比較不分大小寫,字串之間的比較會分, 所以進資料庫前統一轉成小寫加連字號,並且一次決定你的 API 要回帶連字號的 36 字還是不帶的 32 字。

    限制與隱私

    UUID 是識別碼,不是憑證。v4 的 122 個亂數位元的確難猜,但這個值本來就是設計來到處傳的。 把「知道某個 ID」當成「有權限存取」,只要一行 log 外流就等於破口;授權一律在伺服器端查。v7 在這點上更弱,因為前半段就是任何人都讀得懂的時間戳。

    時間排序是有隱私代價的。公開 v7 就等於公開建檔時間,公開一串 v7 還會洩漏你的建檔速度。在意這件事的話,v7 留在內部當主鍵,對外換成 v4 或不透明的代號。

    唯一性的前提是亂數夠好。上面那些碰撞機率的算法,前提是亂數來自密碼學等級的來源。本頁與上面列的標準函式庫都符合,用 Math.random() 湊出來的就不算。

    對人類很不友善。36 個字元沒有任何檢查碼,念不出來,從紙本抄也容易抄錯,螢幕閱讀器更是一個字元一個字元念。 只要流程中有人要處理這個編號,就另外配一組短的人看的編號。

    常見問題

    UUID 真的不會撞嗎?

    理論上會,實際上幾乎不會。要在 v4 中撞兩個 UUID 需要產生約 2^61 個(生日攻擊上限)。即使每秒產生 10 億個,要撞一次也要 73 年。對絕大多數應用,視為「永不碰撞」是合理假設。

    UUID 安全嗎?能當 session token 嗎?

    v4 / v7 用 crypto.getRandomValues() 產生時,隨機性是密碼學等級,可作短期 session token。但 UUID 結構公開(有 version、variant bits),實際亂數位元數是 122 而非 128。長期使用,建議用更長的 token(256-bit 或更多)。

    為什麼 UUID 中間有「4」?

    那是版本標記位元。第 13 個字元固定是版本號(v4 永遠是 4、v7 是 7 等)。第 17 個字元也固定(variant,通常是 8/9/a/b)。剩下的 122 個 bit 才是真正的亂數。

    UUID 跟 ULID / NanoID 差在哪?

    ULID 也是時間排序的 128-bit ID,但用 Base32 編碼成 26 字,比 UUID 的 36 字短。NanoID 是更短的隨機字串(預設 21 字、URL-safe)。三者各有粉絲,不過 RFC 9562 把 UUID v7 標準化後,ULID 的優勢縮小了。

    GUID 跟 UUID 是同一個東西嗎?

    是。GUID(Globally Unique Identifier)是微軟生態系對這組 128-bit 值的叫法,格式與 RFC 9562 定義的 UUID 相同,兩個詞在實務上可以互換。.NET 的 Guid、SQL Server 的 uniqueidentifier 存的都是同一種東西。

    UUID 存進資料庫該用什麼欄位型別?

    有原生型別就用原生型別:PostgreSQL 是 uuid、SQL Server 是 uniqueidentifier,兩者都存 16 個位元組。MySQL 與 SQLite 沒有 UUID 型別,慣例是 BINARY(16) 或 BLOB,在進出時轉換。存成 36 字的文字欄位要 36 個位元組,是二進位的兩倍多,不過在主控台裡好讀很多,小表格這樣存是划算的取捨。

    v7 可以反推出產生時間嗎?

    可以,這同時是功能也是資訊外洩。v7 的前 12 個 16 進位字元就是毫秒級 Unix 時間戳,當成大端整數解出來丟給日期函式,就得到產生的那一刻;貼進本頁的「辨識 UUID」欄會直接顯示本地時間與 UTC。拿到 token 的人也做得到同樣的事,所以產生時間敏感、或不希望外人看出你的建檔速度時,對外請改用 v4。

    資料來源

    • IETF,RFC 9562, Universally Unique IDentifiers (UUIDs)(現行標準:8-4-4-4-12 文字格式、version 與 variant 位元,以及 v6、v7、v8 的排列方式)。
    • IETF,RFC 4122(2005 年的舊規範,已被 RFC 9562 取代,v1、v3、v4、v5 定義在這裡)。
    • MDN,Crypto.randomUUID()與Crypto.getRandomValues()(本頁用的兩個瀏覽器 API,含 secure context 的限制)。
    • 資料庫與語言的行為以各產品官方手冊為準:PostgreSQL 的資料型別與函式章節、MySQL 的 UUID_TO_BIN 說明、SQL Server 的 uniqueidentifier 文件,以及 Python、Java、.NET 的標準函式庫文件。版本支援會變,請對照你實際執行的版本。

    更多開發者工具

    識別碼藏在 API 回應裡看不清楚時,用「JSON 格式化」排版;想看帶著這個 ID 當 subject 的 token 內容,用「JWT Decode 解碼」。值要放進網址或標頭,先過「URL 編碼解碼」與「Base64 編碼解碼」。如果你要的其實是密鑰而不是識別碼,請改用「密碼產生器」。要一整批假資料的話,「台灣測試資料產生器」連姓名、地址、手機一起給。

    相關工具

    本工具用瀏覽器原生 crypto.randomUUID() 與 crypto.getRandomValues()產生,亂數品質為密碼學等級。所有 UUID 在你的瀏覽器內產生,不會傳到任何伺服器。分析只記錄產了幾個、哪個版本,以及複製鈕被按了幾次,不含識別碼本身。