cv.cm

知识库 · 文本、数据与安全

UUID 是什么?

阅读约 5 分钟

简短回答

UUID(通用唯一识别码)是一个 128 位的数字,用来标识数据,无需中央机构统一分配 ID。它写成五组共 32 个十六进制数字,例如 f47ac10b-58cc-4372-a567-0e02b2c3d479。现行标准 RFC 9562 定义了多个版本:版本 4 完全随机;版本 7 以时间戳开头,因此 ID 可以按创建时间排序。

打开UUID 生成

怎样读懂一个 UUID

文本形式共 36 个字符:32 个十六进制数字加 4 个连字符,按 8-4-4-4-12 分组。二进制形式为 16 个字节。每个 UUID 都有两个位置携带元数据:

  • 版本:第三组的第一位数字。在 f47ac10b-58cc-4372-a567-0e02b2c3d479 中是 4。
  • 变体:第四组的第一位数字。标准 UUID 的这一位是 8、9、a 或 b;这里是 a。

GUID 是微软对同一事物的叫法,格式完全相同。十六进制大小写等价;RFC 9562 建议输出用小写、输入不区分大小写。另有两个特殊值:全为零的 Nil UUID,以及全为 f 的 Max UUID。

UUID 的各个版本

RFC 9562 于 2024 年 5 月发布,取代了 RFC 4122,保留了版本 1 到 5,并新增了版本 6、7 和 8。

版本组成可按时间排序典型用途
1时间戳(自 1582 年起的 100 纳秒数)+ 时钟序列 + 节点 ID(通常是 MAC 地址)文本形式不行遗留系统
3命名空间和名称的 MD5 哈希否确定性 ID(建议改用 v5)
4122 个随机位否通用 ID
5命名空间和名称的 SHA-1 哈希否相同输入总是得到相同的 UUID
6重新排列版本 1 的字段,高位时间在前是从 v1 迁移
748 位 Unix 毫秒时间戳 + 随机位是数据库主键、日志、事件
8自定义、由厂商定义的布局视情况而定实验性或特定应用

版本 3 和 5 是确定性的:用 DNS 名称的命名空间对 example.com 做哈希,在任何机器上都得到同一个 UUID。它们使用 MD5 和 SHA-1 只是为了打散位,并非出于安全目的。

版本 4 与版本 7 对比

位版本 4版本 7
0–47随机Unix 毫秒时间戳
48–51版本 0100(4)版本 0111(7)
52–63随机随机,或亚毫秒计数器
64–65变体 10变体 10
66–127随机随机
随机位数122最多 74

RFC 中的版本 7 示例是 017f22e2-79b0-7cc3-98c4-dc0c0c07398f。它的前 12 位十六进制数 017f22e279b0 表示 Unix 纪元后 1,645,557,742,000 毫秒,即 2022-02-22 19:22:22 UTC。

两者在实际中的区别在于数据库性能。随机的 v4 键会落在 B 树索引的随机位置,导致页分裂,写入分散到整个索引。按时间排序的 v7 键则追加在末尾附近,和自增整数很像,插入速度快,索引也更紧凑。新建数据库主键时,v7 通常是更好的选择;对于不应暴露创建时间的不透明令牌,请用 v4。

「唯一」到底有多唯一?

122 个随机位意味着版本 4 UUID 约有 5.3 × 10^36 种可能。根据生日界限,大约要生成 2.7 × 10^18 个,出现一次碰撞的概率才会达到 50%。每秒生成十亿个,也需要大约 86 年。

现实中的碰撞来自糟糕的随机源,而不是数学本身。用相同随机状态克隆出来的虚拟机,或者基于 Math.random() 这类非密码学函数的生成器,都可能产生重复值。请使用密码学安全的随机源:浏览器和 Node.js 中的 crypto.randomUUID(),Python 中的 uuid.uuid4(),或 PostgreSQL 中的 gen_random_uuid()。

如何存储 UUID

  • 有原生类型就用原生类型,比如 PostgreSQL 的 uuid:只占 16 个字节,而文本要 36 个。
  • 在 MySQL 中用 BINARY(16) 存储;用文本存储会让索引大小翻倍以上。
  • 比较 UUID 时不区分大小写,或在输入时统一转为小写。
  • 不要试图从 v4 UUID 中解析出含义。只有版本位和变体位携带信息。

常见问题

UUID 和 GUID 有什么区别?

实际上没有区别。GUID 是微软使用的名称,GUID 遵循与 UUID 相同的 128 位格式和版本。

两个 UUID 有可能相同吗?

理论上可能;但只要使用安全的随机源生成,实际上不会。在版本 4 UUID 的数量远超百亿亿个之前,发生任何碰撞的概率都可以忽略不计。

应该用哪个版本的 UUID?

数据库主键及其他受益于时间排序的场景用版本 7;ID 不应透露任何信息时用版本 4;同样的输入必须始终生成同样的 ID 时用版本 5。

JavaScript 中如何生成 UUID?

调用 crypto.randomUUID(),它会用密码学安全的生成器返回一个版本 4 UUID。它在现代浏览器的 HTTPS 页面中可用,在 Node.js 19 及以上版本中作为全局函数可用。

全部知识文章