知识库 · 文本、数据与安全
UUID 是什么?
简短回答
UUID(通用唯一识别码)是一个 128 位的数字,用来标识数据,无需中央机构统一分配 ID。它写成五组共 32 个十六进制数字,例如 f47ac10b-58cc-4372-a567-0e02b2c3d479。现行标准 RFC 9562 定义了多个版本:版本 4 完全随机;版本 7 以时间戳开头,因此 ID 可以按创建时间排序。
怎样读懂一个 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) |
| 4 | 122 个随机位 | 否 | 通用 ID |
| 5 | 命名空间和名称的 SHA-1 哈希 | 否 | 相同输入总是得到相同的 UUID |
| 6 | 重新排列版本 1 的字段,高位时间在前 | 是 | 从 v1 迁移 |
| 7 | 48 位 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 及以上版本中作为全局函数可用。