uuid guid generate developer-tools

UUID/GUID 终极指南:全面了解唯一标识符及其应用

深入理解 UUID 与 GUID。了解不同版本(v1, v4, v6, v7)的区别、在现代软件中的应用场景,并一键生成。

什么是 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 树索引的随机位置,这会导致:

  1. 索引页分裂 — 数据库必须频繁分裂索引页以容纳乱序插入。
  2. 缓存抖动 — 随机访问模式使缓冲池失效,导致频繁的磁盘读取。
  3. 写入放大 — 比顺序插入多得多的 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);

最佳实践

  1. 一般用途使用 v4 — 不关心排序时,v4 提供最大简便性。
  2. 数据库主键使用 v7 — 避免索引碎片,获得自然排序。
  3. 确定性 ID 使用 v5 — 需要相同输入始终产生相同 UUID 时(例如按 URL 去重内容)。
  4. 隐私敏感场景避免 v1 — 它会嵌入 MAC 地址和创建时间。
  5. 在数据库中以二进制形式存储 UUID(16 字节) — 而非 varchar(36),以节省空间并提高索引性能。
  6. 除非专门实现 DCE Security,否则不要使用 v2
  7. 在 API 边界验证 UUID 输入 — 防止注入格式错误的标识符。
  8. 使用经过良好测试的库 — 而非自己实现 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 都提供了一种经过实战检验、标准化的方式来唯一且安全地标识您的数据。