UUID - 原理介紹
最近探討一個議題,也就是資料庫的主鍵欄位究竟要有 UUID 還是自動遞增的整數好呢?
那要先了解到底什麼是 UUID。
UUID 介紹
英文又叫做 Universally Unique Identifier,它是由 128 位元組成的一個識別碼,通常是透過 32 個十六進制來表示。
我查資料的時候大部分都說它具有唯一性,真的嗎?
我們來探討一下。
UUID 的版本演化
UUID 其實有多個版本的算法,每個版本的算法皆有差異性,也會影響它們究竟是不是能保持唯一性的關鍵。
-
Version 1 基於時間
透過當前時間戳、機器 MAC 地址生成,因為 MAC 地址是全球唯一的,因此可以間接的保證 UUID 全球唯一,
缺點:
- 會暴露電腦的 MAC adress
- 暴露生成這個 UUID 的時間
- 因為 MAC address 會不會重複還要取決於網咖製造商正確分配唯一的 MAC address,有出錯的功能
- 經證實,可以透過 UUID 回敲建立它的電腦,藉此散播病毒
-
Version 2 DCE 安全
和 Version 1 算法相同,但是會將時間戳的前四位置換成 POSIX 的 UID 或 GID,但是在 UUID 規範中並沒有被明確指定,這版本其實可以選擇忽略過去
-
Version 3 基於 Namespace 和一個字串
透過用戶指定一個命名空間及一個字串,然後利用 MD5 雜湊演算法來生成 UUID
缺點:固定的輸入會產生一致的 UUID
-
Version 4 基於隨機數
根據隨機樹或者偽隨機數生成 UUID,重複的機率是可以被計算出來的,但是該機率極其低微,也是最多被用的版本
-
Version 5 基於 Namespace 和一個字串
基本上 Version 3 一樣,但是使用的演算法為 SHA1,缺點一樣就是輸入一樣會產生一致的 UUID
UUID 唯一性?
如果不考慮版本一跟二,那就只有版本三、四、五可以來當作每一筆資料的主鍵。可是我們都知道主鍵一定要有唯一性,如果是版本三、五只要輸入一樣就會產生相同的 UUID。
所以其實我查到最多的作法是採用版本四的算法來產生唯一性的 UUID,機率嘛,雖然數學式子證明確是極其低微才會重複,我覺得是可以選擇忽略重複的後果。
反正工程師一堆要擔心的事情,比這個發生的機率都大太多了…
Golang UUID 程式庫示範
1 | package main |
這個是由 Google 所開源的 Golang UUID 程式庫,也是我推薦可以使用該程式庫來實現 UUID。上面的程式碼分別就是示範了 Version 1 ~ Version 5 的 UUID。
在這邊可以發現 Version 3 跟 Version 5,都需要丟兩個值進去,分別是 UUID 跟 byte,因為 Version 3 跟 Version 5’上面說過是透過命名空間和一個字串去組成的,其實命名空間就是一個 UUID 的值,因此可以想成用一個 UUID 跟一個字串進行某種演算法在形成一個新的 UUID。
缺點就是只要每次給予相同的輸入,輸出的 UUID 就一定會一樣。比如說你可以這樣實驗:
1 | a, _ := uuid.Parse("5a680224-2ae5-11ea-8051-00155d3db160") |
透過 uuid.Parse 將一個字串型態的 UUID,轉成 UUID 型態,最後在餵給 Version 3 跟 Version 5 去產生新的 UUID,你會發現每一次執行之後產生的 UUID 都是一樣的。
總結
通常會建議如果要以 UUID 唯一性的特性來考量的話,會採用 Version 4 來產生,確保唯一性,重複的機率極其低微這樣~
回歸資料庫主鍵的問題來看,我們假設 UUID 的唯一性是肯定的,那對於資料庫主鍵的問題就轉成效能的問題,以及每個不同場景的應用,但我覺得 UUID 最大的優點就是看起來是無序的,而且不像自動遞增那樣會被看出大致資料庫資料數量以及也猜不到 PK 的值。
不過對於不同的資料庫對於 UUID 的效能其實都會有所差異,等有空來好好探討資料庫主鍵用自動遞增跟 UUID 優缺點。