---
title: UUID - 原理介紹
tags:
- UUID
categories:
- UUID
typora-root-url: UUID-原理介紹
date: 2019-12-30 21:10:13
---
**Note**:如果想看到更多其他文章內容,歡迎到我的個人Blog
https://kennychen-blog.herokuapp.com/

最近探討一個議題,也就是資料庫的主鍵欄位究竟要有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優缺點。