一种格式,多种配方
一个 UUID 始终是同一种形状:128 位,写作 32 个十六进制数字,分成熟悉的 8-4-4-4-12 分组。版本之间改变的,是这些位如何生成。现行标准 RFC 9562(2024 年发布,取代长期服役的 RFC 4122)定义了版本 1 到 8,每一种都是以一种在无中央权威下保持唯一的方式填充 128 位的不同策略。UUID 概览聚焦于你最常用的两种,v4 和 v7;本文则绘制整个家族,让你能够识别并选择其中任何一种。
一个 UUID 如何宣告它的版本
无论哪个版本,都有两个小字段被保留,工具会读取两者。版本编码在四个位中(第三组的首个十六进制数字),因此那里是 4 即表示一个 v4 UUID,是 7 即表示 v7。变体编码在下一组的高位中(第四组的首个十六进制数字,对标准变体而言通常是 8、9、a 或 b)。二者合在一起告诉读者如何解释其余的位,这正是为什么一个工具仅凭检视就能标出一个 UUID 的版本,无需上下文。
基于时间的版本:v1、v6、v7
三个版本编码了一个时间戳,使它们大致按时间有序,这对数据库键是一项有用的性质(见数据库键一文):
- 版本 1 把一个 60 位时间戳与一个时钟序列和一个节点标识符结合,其中节点传统上是机器的 MAC 地址。它能用,但会泄露 MAC 和生成时间,而且它的时间戳字段排序作为文本时不能干净地排序。
- 版本 6 是把 v1 的时间戳字段重排,使字节按时间顺序排序。它作为一种就地改进而存在,供已投入 v1、又想要可排序性的系统使用。
- 版本 7 使用一个直白的毫秒级 Unix 时间戳,后接随机位。它是新的时间有序标识符的现代推荐:可排序、无硬件泄露且简单。对于大多数想要有序键的新工作,v7 就是答案。
基于名称的版本:v3 与 v5
版本 3 和 5 是确定性的:相同的输入总产生相同的 UUID。你对一个命名空间标识符加一个名称做哈希,摘要便成为 UUID。版本 3 使用 ,版本 5 使用 -1;v5 更受青睐,因为 SHA-1 虽在此不为安全而用,却是较不脆弱的选择。当你需要一个从既有数据派生而来的稳定标识符时,这就是要选用的版本,例如为某个给定 URL 或文件名生成一个一致的 UUID,使两个系统独立地算出同一个值。
随机的版本:v4
版本 4 用随机性填充 122 位(另外 6 位是固定的版本和变体位)。它不携带时间戳、不携带 MAC、也无结构,这使它在你只是想要一个不透明、抗碰撞的标识符且不在乎排序时,成为安全的默认。它唯一的缺点恰是这种缺乏顺序,正是它损害了数据库索引的局部性,并促成了 v7。工具的 v4 生成在你的浏览器中使用一个密码学安全的随机源,因此那些值从不接触服务器。
版本 8 与特殊的 UUID
版本 8 保留给自定义或实验用途:只有版本和变体位是固定的,其余由你定义,这让一个系统能够把自己的数据编码成 UUID 的形状,同时仍是一个有效的 UUID。还有两个特殊值需要识别:nil UUID,全为零,和 max UUID,全为一,二者都用作哨兵。版本 2 历史上存在(一种 DCE Security 变体),但在实践中难得一见,也不是你今天会选择的东西。
按意图选择
家族很大,但一旦你说出自己需要什么,选择通常很快。要一个不透明、不可猜测、无排序的标识符,用 v4。要一个从既有数据确定性派生的稳定标识符,用 v5。要一个索引良好、按时间有序的键,用 v7。较旧的基于时间的版本(v1、v6)主要为与既有系统的兼容性而重要,而 v8 则在你确实需要编码自己的结构时备用。当你把一个 UUID 粘进工具、它报出版本时,那一个数字便告诉你是这些配方中的哪一个产生了它。