UUID是Universally Unique IDentifier的缩写,翻译为通用唯一标识符或者全局唯一标识符。对于UUID的描述,下面摘录一下规范文件A Universally Unique IDentifier (UUID) URN Namespace中的一些描述:
UUID(也称为GUID)定义了统一资源名称命名空间。UUID的长度为128比特,可以保证在空间和时间上的唯一性。
动机:
使用UUID的主要原因之一是不需要集中式管理,其中一种格式限定了IEEE 802节点标识符,其他格式无此限制。可以自动化按需生成UUID,应用于多重不同的场景。UUID算法支持极高的分配速率,每台机器每秒钟可以生成超过1000万个UUID,因此它们可以作为事务ID使用。UUID具有固定大小128比特,与其他替代方案相比,它具有体积小的优势,非常适用于各种排序、散列和存储在数据库中,具有编程易用性的特点。
这里只需要记住UUID几个核心特定:
全局时空唯一性
固定长度128比特,也就是16字节(1 byte = 8 bit)
分配速率极高,单机每秒可以生成超过1000万个UUID(实际上更高)
下面就JDK中的UUID实现详细分析一下UUID生成算法。编写本文的时候选用的JDK为JDK11。
再聊UUID前面为了编写简单的摘要,所以只粗略摘录了规范文件里面的一些章节,这里再详细聊聊UUID的一些定义、碰撞概率等等。
UUID定义UUID是一种软件构建的标准,也是开放软件基金会组织在分布式计算环境领域的一部分。提出此标准的目的是:让分布式系统中的所有元素或者组件都有唯一的可辨别的信息,因为极低冲突频率和高效算法的基础,它不需要集中式控制和管理唯一可辨别信息的生成,由此,每个使用者都可以自由地创建与其他人不冲突的UUID。
UUID本质是一个128比特的数字,这是一个位长巨大的数值,理论上来说,UUID的总数量为2^128个。这个数字大概可以这样估算:如果每纳秒产生1兆个不相同的UUID,需要花费超过100亿年才会用完所有的UUID。
UUID的变体与版本UUID标准和算法定义的时候,为了考虑历史兼容性和未来的扩展,提供了多种变体和版本。接下来的变体和版本描述来源于维基百科中的Versions章节和RFC 4122中的Variant章节。
目前已知的变体如下:
变体0xx:Reserved, NCS backward compatibility,为向后兼容做预留的变体
变体10x:The IETF aka Leach-Salz variant (used by this class),称为Leach–Salz UUID或者IETF UUID,JDK中UUID目前正在使用的变体
变体110:Reserved, Microsoft Corporation backward compatibility,微软早期GUID预留变体
变体111:Reserved for future definition,将来扩展预留,目前还没被使用的变体
目前已知的版本如下:
空UUID(特殊版本0),用00000000-0000-0000-0000-000000000000表示,也就是所有的比特都是0
date-time and MAC address(版本1):基于时间和MAC地址的版本,通过计算当前时间戳、随机数和机器MAC地址得到。由于有MAC地址,这个可以保证其在全球的唯一性。但是使用了MAC地址,就会有MAC地址暴露问题。若是局域网,可以用IP地址代替
date-time and MAC address, DCE security version(版本2):分布式计算环境安全的UUID,算法和版本1基本一致,但会把时间戳的前4位置换为POSIX的UID或GID
namespace name-based MD5(版本3):通过计算名字和命名空间的MD5散列值得到。这个版本的UUID保证了:相同命名空间中不同名字生成的UUID的唯一性;不同命名空间中的UUID的唯一性;相同命名空间中相同名字的UUID重复生成是相同的
random(版本4):根据随机数,或者伪随机数生成UUID。这种UUID产生重复的概率是可以计算出来的,还有一个特点就是预留了6比特存放变体和版本属性,所以随机生成的位一共有122个,总量为2^122,比其他变体的总量要偏少
namespace name-based SHA-1(版本5):和版本3类似,散列算法换成了SHA-1
其中,JDK中应用的变体是Leach-Salz,提供了namespace name-based MD5(版本3)和random(版本4)两个版本的UUID生成实现。
UUID的格式在规范文件描述中,UUID是由16个8比特数字,或者说32个16进制表示形式下的字符组成,一般表示形式为8-4-4-4-12,加上连接字符-一共有36个字符,例如:
## 例子 123e4567-e89b-12d3-a456-426614174000 ## 通用格式 xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx其中4比特长度的M和1到3比特长度的N分别代表版本号和变体标识。UUID的具体布局如下:
属性 属性名 长度(bytes) 长度(16进制字符) 内容time_low 时间戳低位 4 8 代表时间戳的低32比特的整数表示
time_mid 时间戳中位 2 4 代表时间戳的中间16比特的整数表示
time_hi_and_version 时间戳高位和版本号 2 4 高位4比特是版本号表示,剩余是时间戳的高12比特的整数表示
clock_seq_hi_and_res clock_seq_low 时钟序列与变体编号 2 4 最高位1到3比特表示变体编号,剩下的13到15比特表示时钟序列
node 节点ID 6 12 48比特表示的节点ID
基于这个表格画一个图:
严重注意,重复三次: