--- title: UUID - 原理介紹 tags: - UUID categories: - UUID typora-root-url: UUID-原理介紹 date: 2019-12-30 21:10:13 --- **Note**:如果想看到更多其他文章內容,歡迎到我的個人Blog https://kennychen-blog.herokuapp.com/ ![](cover.png) 最近探討一個議題,也就是資料庫的主鍵欄位究竟要有UUID還是自動遞增的整數好呢? 那要先了解到底什麼是UUID。 ## UUID介紹 英文又叫做**Universally Unique Identifier**,它是由128位元組成的一個識別碼,通常是透過32個十六進制來表示。 我查資料的時候大部分都說它具有唯一性,真的嗎? 我們來探討一下。 ### UUID的版本演化 UUID其實有多個版本的算法,每個版本的算法皆有差異性,也會影響它們究竟是不是能保持唯一性的關鍵。 + Version 1 基於時間 透過當前時間戳、機器MAC地址生成,因為MAC地址是全球唯一的,因此可以間接的保證UUID全球唯一, 缺點: 1. 會暴露電腦的MAC adress 2. 暴露生成這個UUID的時間 3. 因為MAC address會不會重複還要取決於網咖製造商正確分配唯一的MAC address,有出錯的功能 4. 經證實,可以透過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 程式庫示範 ```go package main import ( "github.com/google/uuid" "log" ) func main() { u1, err := uuid.NewUUID() if err != nil { log.Fatalf("can not generate Version 1 UUID: %v", err) } log.Printf("generate Version 1 UUID: %v", u1) u2, err := uuid.NewDCEGroup() if err != nil { log.Fatalf("can not generate Version 2 UUID: %v", err) } log.Printf("generate Version 2 UUID: %v", u2) u3 := uuid.NewMD5(u1, []byte("Kenny")) log.Printf("generate Version 3 UUID: %v", u3) u4, err := uuid.NewRandom() if err != nil { log.Fatalf("can not generate Version 4 UUID: %v", err) } log.Printf("generate Version 4 UUID: %v", u4) u5 := uuid.NewSHA1(u1, []byte("Kenny")) log.Printf("generate Version 5 UUID: %v", u5) } ``` 這個是由Google所開源的Golang UUID程式庫,也是我推薦可以使用該程式庫來實現UUID。上面的程式碼分別就是示範了Version 1 ~ Version 5的UUID。 在這邊可以發現Version 3 跟 Version 5,都需要丟兩個值進去,分別是UUID跟byte,因為Version 3 跟 Version 5'上面說過是透過命名空間和一個字串去組成的,其實命名空間就是一個UUID的值,因此可以想成用一個UUID跟一個字串進行某種演算法在形成一個新的UUID。 缺點就是只要每次給予相同的輸入,輸出的UUID就一定會一樣。比如說你可以這樣實驗: ```go a, _ := uuid.Parse("5a680224-2ae5-11ea-8051-00155d3db160") u3 := uuid.NewMD5(a, []byte("Kenny")) log.Printf("generate Version 3 UUID: %v", u3) u5 := uuid.NewSHA1(a, []byte("Kenny")) log.Printf("generate Version 5 UUID: %v", u5) ``` 透過uuid.Parse將一個字串型態的UUID,轉成UUID型態,最後在餵給Version 3 跟 Version 5去產生新的UUID,你會發現每一次執行之後產生的UUID都是一樣的。 ## 總結 通常會建議如果要以UUID唯一性的特性來考量的話,會採用Version 4來產生,確保唯一性,重複的機率極其低微這樣~ 回歸資料庫主鍵的問題來看,我們假設UUID的唯一性是肯定的,那對於資料庫主鍵的問題就轉成效能的問題,以及每個不同場景的應用,但我覺得UUID最大的優點就是看起來是無序的,而且不像自動遞增那樣會被看出大致資料庫資料數量以及也猜不到PK的值。 不過對於不同的資料庫對於UUID的效能其實都會有所差異,等有空來好好探討資料庫主鍵用自動遞增跟UUID優缺點。