簡介
UUID 的目的是讓分布式系統(tǒng)中的所有元素价淌,都能有唯一的辨識信息薄嫡,而不需要通過中心節(jié)點指定封断,無需考慮數(shù)據(jù)庫創(chuàng)建時的名稱重復問題掀序。
目前最廣泛應用的 UUID 是 RFC4122 協(xié)議規(guī)范的而昨,有時 GUID 也特指是微軟對標準 UUID 的實現(xiàn)救氯,其實 RFC4122 的作者之一也是微軟員工。
規(guī)范標準
Universally Unique IDentifier, UUID 是一個 128 位的數(shù)字歌憨,一般通過 32 個十六進制表示径密,被連字符分為五段,例如: 00d460f0-ec1a-4a0f-a452-1afb4b5d1686
躺孝。詳細標準可以參考 RFC4122 。
為了保證UUID的唯一性底桂,規(guī)范定義了包括網(wǎng)卡MAC地址植袍、時間戳、名字空間(Namespace)籽懦、隨機或偽隨機數(shù)于个、時序等元素,以及從這些元素生成UUID的算法暮顺。UUID的復雜特性在保證了其唯一性的同時厅篓,意味著只能由計算機生成。
版本
如上捶码,1 個 UUID 被連字符分為五段羽氮,形式為 8-4-4-4-12 的 32 個字符,其中的字母是 16 進制表示惫恼,而且大小寫無關(guān)档押。
UUID 本身也經(jīng)過了多個版本的演化,每個版本的算法都不同,其標準格式如下令宿。
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
M: 表示版本號叼耙,只會是1 2 3 4 5
N: 只會是 8 9 a b
Version 1, based on timestamp and MAC address (RFC 4122)
Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1)
Version 3, based on MD5 hashing (RFC 4122)
Version 4, based on random numbers (RFC 4122)
Version 5, based on SHA-1 hashing (RFC 4122)
如下,簡單介紹各個版本的實現(xiàn)方法粒没。
V1 基于時間
通過當前時間戳筛婉、機器 MAC 地址生成,因為 MAC 地址是全球唯一的癞松,從而間接的可以保證 UUID 全球唯一爽撒,不過它暴露了電腦的 MAC 地址和生成這個 UUID 的時間,從而一直被詬病拦惋。V2 DCE安全
和基于時間的 UUID 算法相同匆浙,但會把時間戳的前 4 位置換為 POSIX 的 UID 或 GID,不過這個版本在 UUID 規(guī)范中沒有明確指定厕妖,基本不會實現(xiàn)首尼。V3 基于命名空間
由用戶指定一個命名空間和一個具體的字符串,然后通過MD5
散列來生成 UUID 言秸。不過這個版本按照規(guī)范描述软能,主要是是為了向后兼容,所以也很少用到举畸。V4 基于隨機數(shù)
根據(jù)隨機數(shù)或者偽隨機數(shù)生成 UUID 查排,這個版本也是有意或者無意之中 使用最多 的。V5 基于名字空間
其實和版本 3 類似抄沮,不過散列函數(shù)換成了SHA1
跋核。
Golang實現(xiàn)
基于linux系統(tǒng)命令的實現(xiàn)
如果程序運行在linux系統(tǒng)上,可以使用系統(tǒng)命令 /usr/bin/uuidgen
生成uuid
> /usr/bin/uuidgen
89A26769-0582-447D-AB6B-F98082D2CA46
package main
import (
"fmt"
"log"
"os/exec"
)
func main() {
out, err := exec.Command("uuidgen").Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s \n", out)
}
// 89A26769-0582-447D-AB6B-F98082D2CA46
-
基于 google 的包的實現(xiàn)
install:go get -u -v github.com/google/uuid
package main
import (
"fmt"
"github.com/google/uuid"
"log"
)
func main() {
// V1 基于時間
u1, err := uuid.NewUUID()
if err != nil {
log.Fatal(err)
}
fmt.Println(u1.String())
// V4 基于隨機數(shù)
u4 := uuid.New()
fmt.Println(u4.String()) // a0d99f20-1dd1-459b-b516-dfeca4005203
}
google/uuid V4 源碼分析
package myuu
import (
"crypto/rand"
"encoding/hex"
"io"
)
// 遵循 RFC4122 標準叛买,UUID為128 bit (16 字節(jié))
type UUID [16]byte
// `rand.Reader`是一個全局砂代、共享的密碼用強隨機數(shù)生成器
var rander = rand.Reader
// 定義一個類型為UUID的空值
var Nil UUID
func New() UUID {
return Must(NewRandom())
}
// 發(fā)生異常時觸發(fā) panic
// V1版本此處不觸發(fā)panic,而是返回error
func Must(uuid UUID, err error) UUID {
if err != nil {
panic(err)
}
return uuid
}
func NewRandom() (UUID, error) {
return NewRandomFromReader(rander)
}
// `io.ReadFull` 從 `rand.Reader` 精確地讀取len(uuid)字節(jié)數(shù)據(jù)填充進uuid
func NewRandomFromReader(r io.Reader) (UUID, error) {
var uuid UUID
_, err := io.ReadFull(r, uuid[:])
if err != nil {
return Nil, err
}
// 設(shè)置uuid版本信息
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
return uuid, nil
}
func (uuid UUID) String() string {
var buf [36]byte
encodeHex(buf[:], uuid)
return string(buf[:])
}
// 按照 8-4-4-4-12 的規(guī)則將 uuid 分段編碼率挣,使用 - 連接
func encodeHex(dst []byte, uuid UUID) {
hex.Encode(dst, uuid[:4])
dst[8] = '-'
hex.Encode(dst[9:13], uuid[4:6])
dst[13] = '-'
hex.Encode(dst[14:18], uuid[6:8])
dst[18] = '-'
hex.Encode(dst[19:23], uuid[8:10])
dst[23] = '-'
hex.Encode(dst[24:], uuid[10:])
}