什么是 UUID?
UUID(通用唯一标识符,Universally Unique Identifier),在微软术语中也称为 GUID(全局唯一标识符,Globally Unique Identifier),是一个 128 位的数字,用于在计算机系统中唯一标识信息。与需要中央权威机构分配下一个值的自增整数不同,UUID 可以由任何机器在任何时间独立生成,碰撞概率极低。
UUID 的标准文本表示如下:
550e8400-e29b-41d4-a716-446655440000
^^^^^^^^ ^^^^ ^^^^ ^^^^ ^^^^^^^^^^^^
时间 时间 版本 变体 节点
这串带连字符的字符串将 32 个十六进制数字(128 位)编码为 xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx 的格式,其中:
- M 是版本位(1、3、4、5 或 7)
- N 是变体位(RFC 4122 规定为 8、9、a 或 b)
简史
通用唯一标识符的概念起源于 20 世纪 80 年代末的 Apollo Computer 和 数字设备公司(DEC),作为**网络计算系统(NCS)和分布式计算环境(DCE)**的一部分。其目标是让分布式系统无需任何中央注册机构即可创建标识符。
微软将这一概念用于 COM/OLE,并将其命名为 GUID,但底层结构与 UUID 完全相同,两个术语在实践中可以互换使用。
正式规范随着 RFC 4122 的发布而到来,该规范由 IETF 于 2005 年 7 月发布,标准化了五个 UUID 版本(v1–v5)并定义了变体位。二十年后,RFC 9562(2024 年 5 月发布)取代了 RFC 4122,并正式添加了 UUID v6 和 v7,解决了早期版本在数据库性能方面的不足。
UUID 结构:128 位详解
UUID 始终精确地占用 128 位(16 字节)。格式化为字符串时,它是 36 个字符(32 个十六进制数字 + 4 个连字符)。
字段 位数 十六进制位数 描述
──────────────────────────────────────────────────────
time_low 32 8 时间戳低 32 位(v1)/ 数据
time_mid 16 4 时间戳中间 16 位(v1)/ 数据
time_hi_and_version 16 4 时间戳高 12 位 + 4 位版本号
clock_seq_hi_res 8 2 变体位 + 时钟序列高位
clock_seq_low 8 2 时钟序列低位
node 48 12 节点 ID(v1 中为 MAC 地址,否则为随机)
版本编码在 time_hi_and_version 字段的最高有效 4 位中(第 13 个十六进制字符)。变体编码在 clock_seq_hi_res 的最高有效 2–3 位中(第 17 个十六进制字符)。
UUID 各版本详解
版本 1 — 基于时间
UUID v1 将 60 位时间戳(自 1582 年 10 月 15 日起,以 100 纳秒为单位)与主机的 MAC 地址以及时钟序列结合在一起。
示例:6ba7b810-9dad-11d1-80b4-00c04fd430c8
优点: 包含时间信息,按创建时间大致可排序。
缺点: 嵌入了 MAC 地址,存在隐私隐患——可以暴露 UUID 的生成时间和地点。不推荐用于面向公众的标识符。
版本 2 — DCE 安全
UUID v2 用 POSIX UID/GID 和域标识符替换了部分时间戳。它在 DCE 1.1 规范中定义,但在实践中极少使用,因为它以牺牲唯一性保证为代价来嵌入域信息。
版本 3 — 基于名称(MD5)
UUID v3 通过使用 MD5 对命名空间 UUID 和名称进行哈希,生成确定性的 UUID。
示例:5df41881-3aed-3515-88a7-2f4a814cf09e(命名空间 DNS + "example.com")
使用场景: 为同一逻辑资源生成稳定、可重现的标识符。如果两次哈希相同的命名空间 + 名称,总是得到相同的 UUID。
注意: MD5 在密码学上较弱。新系统建议使用 v5。
版本 4 — 随机(最常用)
UUID v4 使用 122 位密码学随机数据(其余 6 位用于版本和变体)。
示例:9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d
这是迄今为止最常用的 UUID 版本。它不需要任何协调、网络访问或主机知识。随机性来自 CSPRNG(密码学安全伪随机数生成器)。
版本 5 — 基于名称(SHA-1)
UUID v5 在概念上与 v3 相同,但使用 SHA-1 代替 MD5。
示例:886313e1-3b8a-5372-9b90-0c9aee199e5d(命名空间 DNS + "example.com")
使用场景: 需要确定性、可重现的 UUID,且希望比 MD5 更强的哈希算法时。SHA-1 并非完全密码学安全,但其抗碰撞性远好于 MD5。
版本 7 — 时间有序(推荐用于数据库)
UUID v7 是 RFC 9562 的明星功能。它在最高有效位中嵌入了 48 位 Unix 时间戳(毫秒级),使 UUID 具有 k-可排序性——后生成的 UUID 在排序时位于先生成的 UUID 之后。
示例:018e8f5a-2b3c-7d4e-8f9a-0b1c2d3e4f50
前 12 个十六进制字符(018e8f5a-2b3c)编码了毫秒级时间戳,其余部分为随机数。这使得 v7 非常适合作为数据库主键:它保留了 v4 的全局唯一性,同时具有插入顺序,极大地减少了 B 树索引碎片。
碰撞概率:生日问题
UUID v4 使用 122 位随机数。碰撞概率可以使用生日问题公式估算:
p(n) ≈ 1 − e^(−n²/2N)
其中 N = 2^122 ≈ 5.32 × 10^36(UUID 的总可能数量)。
要有 50% 的碰撞概率,您需要生成大约 **2.71 × 10^18(27.1 亿亿)**个 UUID。以每秒 10 亿个 UUID 的速度生成,大约需要 86 年。
实际上,UUID v4 的碰撞概率极低,在所有现实应用中都可以视为不可能发生。
UUID 与自增 ID 的对比
| 特性 | UUID v4 | 自增 ID |
|---|---|---|
| 唯一性范围 | 全局 | 本地(每张表) |
| 生成位置 | 客户端 | 服务端 |
| 可预测性 | 不可预测 | 顺序可预测 |
| URL 安全性 | 是(需编码) | 是 |
| 数据库索引性能 | 差(随机) | 优秀(顺序) |
| 合并/复制 | 容易 | 易冲突 |
| 存储大小 | 16 字节(二进制) | 4–8 字节 |
| 人类可读性 | 低 | 高 |
| 安全性(枚举攻击) | 安全 | 易受攻击 |
自增 ID 对于无分布式需求的单数据库应用来说完全没问题。UUID 在以下情况更具优势:
- 多个服务或数据库需要独立生成 ID。
- 需要合并来自不同来源的记录。
- 希望避免在 URL 中暴露顺序 ID(防止枚举攻击)。
数据库性能:v4 与 v7 对比
UUID v4 在数据库中的问题
由于 UUID v4 是随机的,每条新记录都会插入到 B 树索引的随机位置,这会导致:
- 索引页分裂 — 数据库必须频繁分裂索引页以容纳乱序插入。
- 缓存抖动 — 随机访问模式使缓冲池失效,导致频繁的磁盘读取。
- 写入放大 — 比顺序插入多得多的 I/O 操作。
在大型表(超过 1000 万行)上的基准测试通常显示,UUID v4 主键在插入密集型工作负载中的性能比顺序键差 3–5 倍。
UUID v7 如何解决这个问题
UUID v7 在同一毫秒窗口内是单调递增的。新记录插入到索引的末尾或附近,类似于自增。结果是:
- 近乎零的索引碎片。
- 最优的缓冲池利用率。
- 与自增相当的插入性能。
建议: 对于任何需要全局唯一主键的新数据库架构,使用 UUID v7。
分布式系统使用场景
UUID 是分布式身份的基础:
- 微服务: 每个服务可以独立生成 ID,无需访问中央序列服务器。
- 事件溯源: 事件获得不可变的 ID,在重放和重建时保持唯一。
- CQRS: 命令和查询可以通过客户端生成的 UUID 进行关联。
- 多区域数据库: 在不同区域创建的记录永远不会冲突,使最终一致性和合并操作变得简单。
- 幂等性键: API 可以使用客户端生成的 UUID 作为幂等性键,安全地重试请求。
- 内容寻址系统: UUID v3/v5 可以为跨系统的同一资源生成稳定标识符。
ULID:现代替代方案
ULID(通用唯一词典序可排序标识符)是 UUID 的社区开发替代方案,默认 URL 安全且始终可排序。
| 特性 | UUID v4 | UUID v7 | ULID |
|---|---|---|---|
| 可排序 | 否 | 是 | 是 |
| URL 安全 | 需编码 | 需编码 | 是(Crockford Base32) |
| 时间组件 | 无 | 有(毫秒) | 有(毫秒) |
| 标准 | RFC 9562 | RFC 9562 | 社区规范 |
| 格式长度 | 36 字符 | 36 字符 | 26 字符 |
| 毫秒内单调递增 | 否 | 可选 | 是 |
ULID 格式如 01ARZ3NDEKTSV4RRFFQ69G5FAV——26 个字符,无连字符,可直接在 URL 中使用。如果您需要开箱即用的可排序性和 URL 安全性,且不担心 RFC 兼容性,ULID 值得考虑。
在不同语言中生成 UUID
JavaScript / TypeScript(Node.js 和浏览器)
import { v4 as uuidv4, v7 as uuidv7 } from 'uuid';
const randomId = uuidv4(); // "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
const sortableId = uuidv7(); // "018e8f5a-2b3c-7d4e-8f9a-0b1c2d3e4f50"
// 原生 crypto(Node 14.17+ / 现代浏览器)
const nativeId = crypto.randomUUID(); // UUID v4
Python
import uuid
# v4 — 随机
print(uuid.uuid4()) # "f47ac10b-58cc-4372-a567-0e02b2c3d479"
# v5 — 从命名空间 + 名称确定性生成
print(uuid.uuid5(uuid.NAMESPACE_DNS, 'example.com'))
# "886313e1-3b8a-5372-9b90-0c9aee199e5d"
Go
import "github.com/google/uuid"
id := uuid.New() // UUID v4
v7, _ := uuid.NewV7() // UUID v7(google/uuid v1.6+)
fmt.Println(id.String())
Java
import java.util.UUID;
UUID v4 = UUID.randomUUID();
System.out.println(v4); // "3e4666bf-d5e5-4aa7-b8ce-cefe41c7568a"
Rust
use uuid::Uuid;
let v4 = Uuid::new_v4();
let v7 = Uuid::now_v7();
println!("{}", v4);
println!("{}", v7);
最佳实践
- 一般用途使用 v4 — 不关心排序时,v4 提供最大简便性。
- 数据库主键使用 v7 — 避免索引碎片,获得自然排序。
- 确定性 ID 使用 v5 — 需要相同输入始终产生相同 UUID 时(例如按 URL 去重内容)。
- 隐私敏感场景避免 v1 — 它会嵌入 MAC 地址和创建时间。
- 在数据库中以二进制形式存储 UUID(16 字节) — 而非 varchar(36),以节省空间并提高索引性能。
- 除非专门实现 DCE Security,否则不要使用 v2。
- 在 API 边界验证 UUID 输入 — 防止注入格式错误的标识符。
- 使用经过良好测试的库 — 而非自己实现 UUID 生成,熵源很容易出错。
常见问题解答
Q:UUID 和 GUID 是同一个东西吗?
A:功能上是的。GUID 是微软对同一 128 位标识符格式的命名。结构完全相同,只是术语不同。
Q:两个 UUID 可能相同吗?
A:理论上可以,但对于 v4 来说概率可以忽略不计。您需要生成 27.1 亿亿个 UUID 才有 50% 的碰撞概率。实际上,您永远不会遇到碰撞。
Q:数据库主键应该使用哪个 UUID 版本?
A:UUID v7 是最佳选择。它是时间有序的(有利于 B 树索引),全局唯一,并在 RFC 9562 中标准化。如果您的库还不支持 v7,可以使用 ULID 或"COMB"UUID 模式作为替代方案。
Q:UUID v4 是否足够安全用作会话令牌?
A:UUID v4 有 122 位随机性,通常足够。但是,专用会话令牌库可能使用稍多的熵或更好的编码。对于高安全性场景,建议使用专用令牌生成器。
Q:如何在 SQL 数据库中高效存储 UUID?
A:如果可用,使用原生 UUID 列类型(PostgreSQL、MySQL 8+),或使用 BINARY(16) 列。避免使用占用 2.25 倍空间且索引较慢的 CHAR(36) / VARCHAR(36)。
Q:UUID v3 和 v5 有什么区别?
A:两者都是基于名称且确定性的。v3 使用 MD5,v5 使用 SHA-1。SHA-1 具有更好的抗碰撞性,因此新系统首选 v5。两者都不应用于安全关键的哈希场景。
Q:UUID v7 会取代 UUID v4 吗?
A:对于数据库主键,是的——v7 严格优于 v4。对于其他使用场景(例如 API 密钥、令牌、关联 ID),顺序无关紧要,v4 仍然完全适用。
总结
UUID 是现代软件工程中的基础组件。理解各版本之间的差异有助于做出正确的权衡:
- v4 — 零协调下实现通用唯一性的最佳选择。
- v5 — 确定性、可重现标识符的最佳选择。
- v7 — 由于时间有序性,数据库主键的最佳选择。
无论您是在构建单体应用、微服务架构,还是全球分布式数据库,UUID 都提供了一种经过实战检验、标准化的方式来唯一且安全地标识您的数据。