uuid, a simple introduction

uuid,全称Universally Unique IDentifier,是一种id生成方式,我们使用它来生成一唯一的id. 同时,它也被称为guid. uuid有若干生成方法,本文介绍的是一种基于时间的方法(也就是version 1).协议是rfc4122. 在java中,它是系统内置的class.而在c++中,我们想要实现,其实也不困难.

如rfc4122中所示,uuid是一个长为128bit的结构如下的一串数字.

_0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time_low |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time_mid | time_hi_and_version |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|clk_seq_hi_res | clk_seq_low | node (0-1) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| node (2-5) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

其中time是指UTC 1582年10月15日00:00:00 开始到如今的,以100纳秒为单位的时间数,它是一个60bit长的一个值,所以被存到了几段地方.

clk_seq按情况而定,若可以读状态,那么clk_seq是上个状态的clk_seq+1,否则是随机出来的一个0x3FFF范围中的数字.

node是自定义的一个顺序.

然后一路拼接,得到两个uint64_t类型的数据.输出结果若用string表示,格式应该是这样的:

UUID = time-low "-" time-mid "-"
time-high-and-version "-"
clock-seq-and-reserved
clock-seq-low "-" node
time-low = 4hexOctet
time-mid = 2hexOctet
time-high-and-version = 2hexOctet
clock-seq-and-reserved = hexOctet
clock-seq-low = hexOctet
node = 6hexOctet
hexOctet = hexDigit hexDigit
hexDigit =
"0" / "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" /
"a" / "b" / "c" / "d" / "e" / "f" /
"A" / "B" / "C" / "D" / "E" / "F"

一个简单例子如下:

f81d4fae-7dec-11d0-a765-00a0c91e6bf6

rfc4122给了个官方实现示例,我也写了个c++版本的简易生成器,里面有些代码还是值得商榷的,比如clk_seq我是直接random的.其实应该检查状态的.代码在osx和centos 64上都编译没有问题.

在java/scala中,若要读取uuid,一般我用fromString.

scala> java.util.UUID.fromString("f81d4fae-7dec-11d0-a765-00a0c91e6bf6")
res0: java.util.UUID = f81d4fae-7dec-11d0-a765-00a0c91e6bf6

需要注意的是,uuid中的timestamp和普通的java.util.date中的timestamp单位是不一样的,我们已经说过,uuid中是UTC 1582年10月15日00:00:00至今的100纳秒数,而date中的是UTC1970年1月1日00:00:00至今的毫秒数.我们从uuid中获取ts方法应该是这样的:

scala> val uuid = java.util.UUID.fromString("f81d4fae-7dec-11d0-a765-00a0c91e6bf6")
uuid: java.util.UUID = f81d4fae-7dec-11d0-a765-00a0c91e6bf6

scala> val ts = uuid.timestamp
ts: Long = 130742845922168750

scala> val ts4j = ( ts - 0x01b21dd213814000L ) / 10000
ts4j: Long = 854991792216

scala> val jdate = new java.util.Date(ts4j)
jdate: java.util.Date = Mon Feb 03 12:43:12 EST 1997

References

https://tools.ietf.org/html/rfc4122

http://docs.oracle.com/javase/7/docs/api/java/util/UUID.html

https://support.datastax.com/hc/en-us/articles/204226019-Converting-TimeUUID-Strings-to-Dates