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
- 先選版本。這批 ID 會進資料表、而且你會照建檔順序查,就選 v7;其他情況 v4 就夠。
- 把數量(最多 1000)填成你要的筆數。欄位收 1 到 1000,數字一改清單就跟著重產。
- 按重新產生換一批相同設定的新值。本頁不記任何東西,重新整理就是另一批。
- 按全部複製。複製出來是一行一個,試算表欄位、SQL 的
VALUES清單與多數 CSV 匯入都吃這個格式;每一列旁邊的複製鈕則只複製那一個。 - 關掉分頁前先貼到存得住的地方,這裡不留紀錄,關掉就找不回來。
測試的 fixture 建議把值寫死在程式碼裡,不要在測試執行時才呼叫產生器。 測試失敗時,你才能直接拿那串常數去全專案搜尋。
怎麼把 v7 當主鍵而不會後悔
- 欄位型別優先用 16 個位元組的原生型別:PostgreSQL 的
uuid、SQL Server 的uniqueidentifier、MySQL 的BINARY(16)、SQLite 的BLOB或TEXT。 - 資料庫沒有 v7 函式時就在應用層產。插入前就知道 ID 的好處是,你可以在記憶體裡把整組關聯物件組好,再用一個 transaction 寫進去。
- 建檔時間敏感的資料別把 v7 放進對外網址。v7 等於公告這筆資料何時建立,連續幾筆還會洩漏你的建檔速度。
- 另外給人看的要有短編號,例如發票號碼或訂單編號。沒有人想在電話裡念 36 個字元。
- 索引要克制。每一個次要索引都會複製一份 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.getRandomValues() 產生密碼學等級的亂數密碼,可選長…
- Base64 編碼解碼
文字 ↔ Base64 雙向編碼解碼,完整支援 UTF-8 中文與特殊字元。
- JWT Decode 解碼
JWT 解碼工具:貼上 JSON Web Token 解出 header 與 payload,過…
- 雜湊計算
計算文字與檔案的 SHA-1、SHA-256、SHA-384、SHA-512 雜湊值,走瀏覽器原…
- 身分證產生器與驗證
台灣身分證產生器與驗證,也支援居留證號(ARC 統一證號):隨機產生測試用合法號碼、驗證並解析縣…
- 台灣測試資料產生器
一鍵產出台灣假姓名、手機、地址、Email 與合法統一編號,一批最多 200 筆。
- 統一編號驗證與產生器
驗證台灣公司統一編號是否合法,逐位列出加權計算過程並產生測試用統編,支援 112/4 起「可被 …
本工具用瀏覽器原生 crypto.randomUUID() 與 crypto.getRandomValues()產生,亂數品質為密碼學等級。所有 UUID 在你的瀏覽器內產生,不會傳到任何伺服器。分析只記錄產了幾個、哪個版本,以及複製鈕被按了幾次,不含識別碼本身。
