之前的项目有需要使用唯一ID的地方,但是之前基本都是uuid,今天看到go语言中文网的推送顺便学习mark一下,同时了解一下分布式ID。推荐其他学习go语言的学习的小伙伴也可以关注,其中的每日一库还是非常有意思。

uuid

// uuid demo
id, _ := uuid.NewV4()
fmt.Println("uuid:",id)

uuid: 04e0a804-b14d-41ab-be10-0752e2537f10

uuid有两种包:

下面简单说下五种版本的区别:

  • Version 1,基于mac地址、时间戳。
  • Version 2,based on timestamp,MAC address and POSIX UID/GID (DCE 1.1)
  • Version 3,Hash获取入参并对结果进行MD5。
  • Version 4,纯随机数。
  • Version 5,based on SHA-1 hashing of a named value。

特点

  1. 5个版本可供选择。
  2. 定长36字节,偏长。
  3. 无序。

shortuuid

id := shortuuid.New()
fmt.Println("id:", id, "length:", len(id))

// 固定不变
id = shortuuid.NewWithNamespace("172.19.0.3")
fmt.Println("id:", id, "length:", len(id))

// NewWithAlphabet函数可以用于自定义的基础字符串,字符串要求不重复、定长57
str := "12345#$%^&*67890qwerty/;'~!@uiopasdfghjklzxcvbnm,.()_+·><"
id = shortuuid.NewWithAlphabet(str)
fmt.Println("id:", id, "length:", len(id))

id: 6Zurk42XGcf3Qko6yTQ65C length: 22
id: GekwccmjXySvxArhLhPx3L length: 22
id: hht,)'r3d,cb(tdty_ryir length: 22

初始值基于uuid Version4;第二步根据alphabet变量长度(定长57)计算id长度(定长22);第三步依次用DivMod(欧几里得除法和模)返回值与alphabet做映射,合并生成id。

特点

  1. 基于uuid,但比uuid的长度短,定长22字节。

xid

// hostname+pid+atomic.AddUint32
id := xid.New()
idOld := id
containerName := "test"
// 由于xid默认使用可重复ip地址填充4 5 6位。
// 实际场景中,服务都是部署在docker中,这里把ip地址位替换成了容器名
// 这里只取了容器名MD5的前3位,验证会重复,放弃使用
containerNameID := make([]byte, 3)
hw := md5.New()
hw.Write([]byte(containerName))
copy(containerNameID, hw.Sum(nil))
id[4] = containerNameID[0]
id[5] = containerNameID[1]
id[6] = containerNameID[2]

fmt.Println("idOld:", idOld, "length:", len(id))
fmt.Println("idNew:", id, "length:", len(id))

idOld: cdkuddo591j42t2os6v0 length: 12
idNew: cdkuddo9htlk2t2os6v0 length: 12

xid是由时间戳、进程id、Mac地址、随机数组成。有序性来源于对随机数部分的原子+1。

特点

  1. 长度短。
  2. 有序。
  3. 不重复。
  4. 时间戳这个随机数原子+1操作,避免了时钟回拨的问题。

时钟回拨:服务器上的时间突然倒退回之前的时间。可能是人为的调整时间;也可能是服务器之间的时间校对。

ksuid

id := ksuid.New()
fmt.Println("id:", id, "length:", len(id))

id: 2HFfFj1A7ufeUcspjQT8CfZ2lC8 length: 20

特点

  1. 由随机数和时间戳组成。时间戳占前4字节,后面均为随机数

ulid

t := time.Now().UTC()
entropy := rand.New(rand.NewSource(t.UnixNano()))
id := ulid.MustNew(ulid.Timestamp(t), entropy)
fmt.Println("id:", id.String(), "length:", len(id.String()))

id: 01GHAVVENJPP05VWE937CYMZ0B length: 26

特点

  1. 随机数和时间戳组成

snowflake

node1, _ := snowflake.NewNode(1)
node2, _ := snowflake.NewNode(2)
id1 := node1.Generate().String()
id2 := node2.Generate().String()
	
fmt.Println("id:", id1, "length:", len(id1))
fmt.Println("id:", id2, "length:", len(id2))

id: 1589855672575463424 length: 19
id: 1589855672575467520 length: 19

雪花算法不会暴露MAC地址更安全、生成的ID也不会过于冗余。雪花的一部分ID序列是基于时间戳的,那么就会有时钟回拨的问题。