UUID 產生器
v4 為 122 位元的隨機值,無法預測,適合不希望 ID 洩漏任何資訊的情境。但順序隨機,不適合作為資料庫主鍵。
您輸入的內容不會被儲存、記錄或分享。
在瀏覽器中產生 UUID,可選擇隨機的 v4 或可依時間排序的 v7。選好數量後可單獨複製或一次全部複製。
使用方式
- 需要隨機值選 v4,需要時間排序選 v7。
- 選擇要產生的數量。
- 單獨複製,或使用「全部複製」。
128 位元裡裝了什麼
UUID 是 16 個位元組,寫成常見的 8-4-4-4-12 分組、共 32 個十六進位數字。並非每個位元都能自由使用:4 個位元代表版本,另外兩到三個位元代表變體,這就是為什麼這裡產生的每個 UUID,第 13 個數字一定是 4 或 7,第 17 個數字一定是 8、9、a 或 b 之一。
版本 4 的 UUID 會以隨機值填滿剩下的 122 個位元。版本 7 則把前 48 個位元用於毫秒時間戳記,其餘 74 個位元填入隨機值,以犧牲部分不可預測性換取順序性。
連字號不帶任何資訊,大小寫也一樣,不過 RFC 9562 規定輸出應使用小寫、輸入則兩者都接受。urn:uuid: 前綴,以及 Windows 工具偏好的大括號,都只是呈現方式,不是內容。
為什麼會有 v7
版本 4 的 UUID 是均勻隨機的,這正是不該透露任何資訊的識別碼所需要的,卻也正是主鍵不想要的。
B 樹索引會依排序順序保存項目。插入一個隨機鍵,它可能落在順序中的任何位置,所以每次插入都會碰到不同的頁面;一旦常用頁面超出記憶體容量,每次插入就不只是寫入,還得先讀取。頁面填得不均勻並不斷分裂,索引變得比它所索引的資料還大,而寫入吞吐量會隨著資料表成長而下降。使用遞增的鍵時,插入都落在尾端,只有一個頁面保持熱門,上述問題都不會發生。
版本 7 把時間戳記放在高位元,讓後產生的 ID 排在先產生的 ID 之後,在保有全域唯一性、而且不需要機器之間協調的前提下,恢復了這種局部性。這就是 RFC 9562 加入 v7 的原因,也是現在主鍵欄位合適的預設選擇。
取捨是真實存在的:v7 UUID 會透露資料列大約何時建立,連續的 ID 還會洩漏順序與大致的產生速率。如果識別碼會公開曝光,而且這兩點任一屬於敏感資訊,v4 仍然是正確選擇。
碰撞
兩個版本 4 UUID 發生碰撞,代表 122 個隨機位元完全重複。讓這個數字比較容易理解的常見方法是生日問題的估算:你得每秒產生大約十億個 UUID,持續約 85 年,整個集合中出現任何一次碰撞的機率才會達到 50%。
所以實務上不會發生。真正可能出錯的地方不在統計,而是:亂數產生器的種子設定不當,例如從同一個快照複製出來的虛擬機器、開機時沒有熵來源的嵌入式裝置;為了塞進欄位而截斷 UUID,毀掉大部分隨機性的程式碼;或是悄悄退回使用非密碼學亂數來源的函式庫。這裡的位元來自瀏覽器的密碼學亂數產生器;在其他地方,好習慣是實際確認你的函式庫用的是什麼,而不是想當然耳。
如何儲存
文字形式有 36 個字元,而值本身只有 16 個位元組。把 UUID 存成 char(36) 會讓空間增加一倍以上;在每個次要索引都帶有主鍵副本的資料庫中,這個成本還會乘到整個結構描述上。
PostgreSQL 有原生的 uuid 型別,以 16 個位元組儲存,輸入時接受文字形式。MySQL 沒有這種型別,慣用做法是使用 binary(16),並在應用程式邊界轉換;對版本 1 UUID,還有一個眾所周知的技巧,就是重排時間位元組以恢復可排序性,而版本 7 讓這個技巧變得不再必要。
如果你在網址中顯示 UUID 又嫌它太長,請用 base32 或 base64url 編碼同樣的 16 個位元組,而不是縮短它的值。截斷看起來一直都沒問題,直到出問題的那一刻。
完整的版本一覽
第 1 版把 60 位元的時間戳記和機器的 MAC 位址編進識別碼裡,因此它既可排序又可追溯來源;後面這個特性正是它失寵的原因,因為它會洩漏是哪一台機器產生了這個識別碼。第 2 版是 DCE 的安全性變體,幾乎沒有人在用。第 3 版和第 5 版是決定性的:它們分別用 MD5 和 SHA-1 對命名空間與名稱做雜湊,所以相同的輸入永遠產生相同的識別碼。
第 4 版純粹由亂數構成,因為不需要任何協調、不需要時鐘、也不需要機器身分,於是到處都成了預設選項。第 6 版是把第 1 版重新排列,讓時間戳記能正確排序;而第 7 版則是現代的答案:高位元放毫秒等級的時間戳記,後面接著亂數,並於 2024 年在 RFC 9562 中標準化。
第 5 版值得比現在更常被使用。當你需要從手上已有的東西衍生出一個穩定的識別碼,例如一個 URL、一條檔案路徑、一個租戶名稱,把它雜湊成 UUID 就能讓每個系統都得到相同的識別碼,不需要對照表,也不需要任何協調步驟。
其他選項,以及它們更適合的場合
ULID 把時間戳記和亂數編成 26 個 Crockford base32 字元:可排序、不分大小寫,文字形式也比 UUID 短。它出現得比第 7 版早,解決的是同一個問題,因此新系統通常直接用 v7、留在標準之內就好。
Snowflake 識別碼最早來自 Twitter,它把時間戳記、機器 id 和序號打包進 64 位元。它精簡、可排序、幾乎不需要協調,代價是每個節點都得有一個獨一無二的 id。它的數值也超出 JavaScript number 能精確表示的範圍,這就是為什麼使用它的 API 都以字串回傳。
nanoid 會產生適合放進網址的短亂數字串,字母表可以自行設定;預設的 21 個字元帶有的熵大致與 v4 UUID 相當,長度卻只有一半。而在不少系統裡,最好的選擇仍然是普通的自動遞增整數:體積最小、建索引最快,而且只要識別碼不對外,它就完全夠用。
識別碼公開之後會洩漏什麼
在公開網址裡使用連續整數,等於把你的業務量告訴所有人,也讓任何人可以沿著範圍逐一嘗試:/orders/1001 就意味著 /orders/1000 存在。再加上少了授權檢查,這就是所謂的不安全的直接物件參考,至今仍是網頁應用程式中最常被發現的缺陷之一。
隨機的 v4 UUID 消除了列舉的可能,但也僅止於此。它不是存取控制:一個沒人猜得到的識別碼,仍然是一個只要拿到就能使用的識別碼,而網址會透過 referrer 標頭、瀏覽紀錄、日誌、螢幕截圖和分享出去的連結外流。每一次請求都還是得檢查這位使用者是否有權查看這項資源。
第 7 版把其中一部分不透明性交換了回去。它會透露一筆資料大約是什麼時候建立的,而連續的識別碼會洩漏順序與產生速率,足以推算一週之內有多少客戶註冊。如果這兩者之一在公開識別碼中屬於敏感資訊,那就在內部用 v7 當主鍵,對外則暴露 v4 或另一組 slug。
主鍵、索引與儲存
B-tree 索引會讓項目保持排序,所以隨機的鍵會把寫入分散到整個索引。一旦索引不再放得進記憶體,每次插入就同時變成一次讀取與一次寫入,頁面填得不均勻而分裂,寫入吞吐量也會隨著資料表變大而下滑。連續的鍵則是往尾端追加,只讓一個頁面保持熱門,完全避開這些問題,這正是採用第 7 版的全部理由。
儲存方式同樣重要。一個 UUID 是 16 位元組,它的文字形式則有 36 個字元。PostgreSQL 有原生的 uuid 型別,以二進位形式儲存,輸入時也接受文字。MySQL 沒有這種型別,因此慣例是使用 BINARY(16),並在應用程式的邊界做轉換。改用文字儲存會讓空間增加超過一倍,而在每個次要索引都會夾帶一份主鍵的引擎中,這個成本會在整個結構裡不斷放大。
常見的折衷做法是:內部的 join 使用小整數主鍵,對外的識別碼則用 UUID 並加上唯一索引。這樣既維持了緊湊的實體排列,又能給外界一個看不出內情的代號。
在程式中安全地產生識別碼
請使用平台提供的函式,不要自己動手拼湊:瀏覽器和 Node 用 crypto.randomUUID,Python 用 uuid4,Java 用 UUID.randomUUID。這些函式取自密碼學等級的產生器,碰撞機率的那套論證才因此站得住腳。而會退回去使用 Math.random 的函式庫,某些較舊的 JavaScript 套件確實如此,產生出來的識別碼看起來一模一樣,實際上卻是可預測的。
真正會出問題的地方並不是統計上的。從同一份快照複製出來的虛擬機可能會以相同的熵池啟動;嵌入式裝置在開機時的熵可能幾乎為零;而為了塞進某個欄位而把 UUID 截斷的程式碼,會摧毀大部分的隨機性,卻留下一個看起來仍然像識別碼的東西。
剩下的問題靠兩個習慣就能避免。比較識別碼時不要分大小寫,因為 RFC 9562 規定輸出用小寫但兩者都要接受;同時記得去掉 Windows 工具會加上的大括號與 urn:uuid: 前綴。另外,請在輸入時做驗證,而不是相信它的外形,因為網址裡一串長得像 UUID 的字串,終究還是使用者輸入。
常見問題
- UUID v4 與 v7 有什麼差別?
- v4 為 122 位元的隨機值,不會洩漏任何資訊也無法預測。v7 開頭是 48 位元的毫秒時間戳,後面才是隨機位元,因此較晚產生的 ID 一定排在較後面。v4 隱藏建立時間,v7 則會透露時間但可排序。
- 資料庫主鍵應該用哪一種?
- v7。隨機的 v4 主鍵會讓寫入分散到整個索引,造成 B-tree 頁面碎片化,影響寫入效能與快取命中率。v7 主鍵依序附加,行為接近自增整數,同時仍保有全域唯一性。
- 這些 UUID 是密碼學等級的隨機嗎?
- 是。隨機位元來自瀏覽器的密碼學安全亂數產生器 crypto.getRandomValues,而非 Math.random。v4 具有 122 位元隨機值,碰撞在實務上不需擔心。
- 為什麼沒有 v1 或 v5?
- v1 會編入 MAC 位址與時鐘狀態,可能洩漏主機身分,RFC 9562 也建議新系統改用 v7。v3 與 v5 屬於名稱型,需以命名空間加名稱雜湊出確定性的 ID,需要額外輸入,較適合獨立成另一個工具。
- UUID 是在伺服器上產生的嗎?
- 不是。全部在您的瀏覽器中產生且不會傳送,因此包含本站在內沒有任何人會看到這些值。
- UUID 與 GUID 有什麼不同?
- 實際上沒有差別。GUID 是微軟對同一種 128 位元識別碼的稱呼,因此 Windows 工具常以大括號形式顯示。需要該格式時可勾選「大括號」選項。
最後更新:
請提供操作步驟、預期結果及實際結果。請移除機密與個人資料。
