什么是 UUID?格式、版本以及 UUID 与 GUID 的区别
UUID(Universally Unique Identifier,通用唯一识别码)是一个 128 位的数字,用来标识数据,而不必向中央服务器申请下一个可用 ID。它通常写成 36 个字符:32 个十六进制数字,按 8-4-4-4-12 分成五组,例如 f47ac10b-58cc-4372-a567-0e02b2c3d479。该格式由 RFC 9562(2024 年 5 月)定义,它取代了 RFC 4122。
免费试用:UUID 生成器 免费使用,无需注册账号。
那么,什么是 UUID 最常见的用途?任何系统都可以自行生成 UUID,即使离线也可以,而且几乎可以确定不会有别人生成同样的值。因此 UUID 常被用作数据库主键、文件名、请求 ID 和消息 ID。本文讲解 UUID 的格式、各个版本、真实的碰撞概率、UUID 与 GUID 的区别,以及如何在 v4 和 v7 之间选择。想边读边试,可以在新标签页中打开免费的 UUID 生成器。
什么是 UUID?它由哪些部分组成(8-4-4-4-12 格式)
128 位就是 16 个字节。每个字节写成两个十六进制数字,共 32 位数字,加上四个连字符,文本形式就是 36 个字符。其中有两个位置含义固定:
f47ac10b-58cc-4372-a567-0e02b2c3d479
^ ^
| +-- 第 17 位 "a":变体(8、9、a 或 b = RFC 9562)
+------- 第 13 位 "4":版本 4(随机)
- 版本(version)是第三组的第一位(第 13 个十六进制数字),说明这个 UUID 是如何生成的。
- 变体(variant)是第四组的第一位(第 17 个十六进制数字)。标准 UUID 中它的最高两位是
10,所以这一位总是8、9、a或b。0–7属于旧的 NCS 标识符,c和d保留给 Microsoft 向后兼容,e和f保留给将来使用。
由于这 6 位是固定的,随机的版本 4 UUID 含有 128 − 6 = 122 个随机位。十六进制字母可以大写、小写或混写,所以 F47AC10B-… 和 f47ac10b-… 是同一个 UUID;最常见的习惯是小写。
UUID 版本:v1 到 v8、Nil 和 Max
RFC 9562 定义了八个版本和两个特殊值:
| 版本 | 构成方式 | 常见用途 |
|---|---|---|
| v1 | 60 位时间戳(自 1582 年起以 100 纳秒为单位)+ 时钟序列 + 节点 ID,传统上是 MAC 地址 | 旧系统 |
| v2 | DCE 安全版本,RFC 中未详细定义 | 很少使用 |
| v3 | 对命名空间 UUID 和名称做 MD5 哈希 | 可重现的 ID(优先用 v5) |
| v4 | 122 个随机位 | 通用默认选择 |
| v5 | 对命名空间 UUID 和名称做 SHA-1 哈希 | 由名称生成可重现的 ID |
| v6 | 与 v1 字段相同,但重新排列使时间戳可排序 | 从 v1 升级 |
| v7 | 48 位 Unix 毫秒时间戳 + 随机位 | 数据库主键、可排序 ID |
| v8 | 自定义布局,只固定版本和变体 | 厂商自定义格式 |
| Nil | 00000000-0000-0000-0000-000000000000 |
表示"无值"的占位符 |
| Max | ffffffff-ffff-ffff-ffff-ffffffffffff |
哨兵值或上界 |
基于名称的版本是确定性的。DNS 命名空间加上名称 example.com,无论在哪台机器、哪种语言中,都会得到 v5 UUID cfbff0d1-9375-5685-968c-48ce8b15ae17。当同样的输入必须始终对应同一个 ID 时,就用它们。
版本 7 的 UUID 把时间放在最前面。下面这个例子生成于 2026-09-24 12:00:00 UTC,即 Unix 纪元之后 1,790,251,200,000 毫秒,十六进制为 0x01a0d3496e00:
01a0d349-6e00-7c3f-9d21-4b6e8a1f0c57
^^^^^^^^^^^^^ ^ ^
| | +-- 变体位 "9"
| +------- 版本 7
+--------------------- 48 位毫秒时间戳
其余 74 位是随机数(有些库会把其中一部分用作计数器)。
UUID 是唯一的吗?v4 的碰撞概率
UUID 并不保证唯一,只是以压倒性的概率唯一。122 个随机位意味着共有 2^122 ≈ 5.3 × 10^36 个可能的 v4 值。用生日问题的近似公式,可以算出要让"至少两个相同"的概率达到 50%,需要的 UUID 数量 n:
n ≈ √(2 · ln 2 · 2^122) ≈ 2.71 × 10^18
也就是约 271 亿亿个 UUID。即使每秒生成 10 亿个,也要大约 86 年才能达到这个数量。在现实的数量级下,风险可以忽略:
| 已生成的 v4 UUID 数量 | 出现任意碰撞的概率 |
|---|---|
| 10 亿(10^9) | 约 9.4 × 10^-20 |
| 1 万亿(10^12) | 约 9.4 × 10^-14 |
| 103 万亿(1.03 × 10^14) | 约十亿分之一 |
| 2.71 × 10^18 | 约 50% |
这些数字的前提是随机数生成器质量良好。实际中的碰撞通常来自程序错误:随机数生成器种子设置不当、克隆的虚拟机复用了同样的状态,或者代码复制了已有 ID 而不是生成新的。因此数据库中的唯一约束仍然值得保留。
UUID 与 GUID 的区别是什么?
实际上没有区别。GUID(Globally Unique Identifier,全局唯一标识符)是 Microsoft 对同一种 128 位值的称呼,用于 Windows、COM、.NET 和 SQL Server。例如 .NET 的 Guid.NewGuid() 生成的就是普通的版本 4 UUID。区别只在显示和存储方式上:
- 文本写法。 Microsoft 工具常把 GUID 显示为大写并加上花括号,例如
{F47AC10B-58CC-4372-A567-0E02B2C3D479}。它与小写形式是同一个值。 - 二进制字节序。 Microsoft 的 GUID 结构把值分成一个 32 位字段、两个 16 位字段和 8 个单独字节。在小端(little-endian)系统上,以及 .NET 的
Guid.ToByteArray()中,前三个字段的字节顺序是反过来的,后 8 个字节保持原顺序。这通常称为混合字节序(mixed-endian):
文本形式: f47ac10b-58cc-4372-a567-0e02b2c3d479
RFC 顺序: f4 7a c1 0b | 58 cc | 43 72 | a5 67 0e 02 b2 c3 d4 79
微软顺序: 0b c1 7a f4 | cc 58 | 72 43 | a5 67 0e 02 b2 c3 d4 79
文本完全相同,只是 16 个原始字节不同。如果在 Microsoft 系统和期望 RFC 字节序的系统之间直接复制二进制 GUID,值就会错乱。应通过文本形式转换,或者在 .NET 8 及以上版本中使用 ToByteArray(bigEndian: true)。Microsoft 在 Guid.ToByteArray 参考文档中说明了这些字节组的反转。
所以"UUID 还是 GUID"只是叫法问题,并不需要你做选择。
主键该用 UUID v4 还是 v7?
两者都是 128 位 UUID,但在索引中的表现差别很大。
- v4 是随机的,每条新记录都会落在 B 树索引中的随机位置。插入操作会触及整个索引的各个页面,导致页分裂、更多写入,以及更大的、必须常驻内存的工作集。
- v7 以时间戳开头,新 ID 总比旧 ID 大。插入发生在索引末尾,类似自增整数,同一时间创建的记录也会存放在一起。
v7 还能按 ID 排序得到创建顺序,并直接从 ID 中读出创建时间。这也是它的缺点:任何看到 v7 ID 的人都能知道记录的创建时间,精确到毫秒。对需要隐藏时间信息的公开标识符使用 v4,对写入量大的大表内部主键使用 v7。
如何高效存储 UUID
存储 16 个字节,而不是 36 个字符的文本。文本列每个值至少占用 36 个字节,是二进制的两倍多,而且每个包含该键的索引都会随之变大。
- PostgreSQL:使用原生
uuid类型(16 字节)。 - SQL Server:使用
uniqueidentifier(16 字节)。 - MySQL:使用
BINARY(16),并用UUID_TO_BIN()和BIN_TO_UUID()转换。
只在系统边界(如 API 和日志)才转换成文本。
如何在代码中生成 UUID
大多数语言都内置了生成器:
import uuid
uuid.uuid4() # random v4
uuid.uuid7() # v7, Python 3.14+
uuid.uuid5(uuid.NAMESPACE_DNS, "example.com") # cfbff0d1-9375-5685-968c-48ce8b15ae17
crypto.randomUUID(); // v4, in browsers (HTTPS pages) and Node.js
SELECT gen_random_uuid(); -- v4, built in since PostgreSQL 13
SELECT uuidv7(); -- v7, PostgreSQL 18+
Guid.NewGuid(); // v4
Guid.CreateVersion7(); // v7, .NET 9+
crypto.randomUUID() 的说明见 MDN。在浏览器中,它只能在安全上下文(HTTPS 页面)中使用。
如何用正则表达式校验 UUID
下面的模式匹配版本 1–8、标准变体的规范格式(请使用不区分大小写的匹配):
^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
[1-8] 检查版本位,[89ab] 检查变体位。如果还想接受 Nil 和 Max UUID、花括号或 urn:uuid: 前缀,需要另外添加检查。
UUID 不是秘密
UUID 用于标识,而不是保护。RFC 9562 指出,实现不应假定 UUID 难以猜测,也不得把 UUID 当作安全凭据使用。v1 会暴露创建时间,传统上还会暴露生成它的机器的 MAC 地址;v7 会暴露创建时间。即使是安全生成器产生的 v4,也不是为密码重置令牌或 API 密钥设计的。这类用途应使用专门的随机令牌,例如用 令牌生成器 生成,并且每次请求时仍要检查权限。
UUID 的替代方案
ULID 是一种目标相近的常见替代方案:同样是 128 位,包含 48 位毫秒时间戳和 80 个随机位,写成 26 个 Crockford Base32 字符,因此按文本排序即可得到时间顺序。如果你需要这种格式,ULID 生成器 可以生成、解码 ULID,并把它们转换成 UUID。对于新项目,UUID v7 提供同样的时间排序,同时保留数据库原生支持的标准 UUID 格式。
在线生成和检查 UUID
免费的 UUID 生成器 完全在浏览器中运行。它可以生成 v1、v3、v4、v5 和 v7 UUID 以及 Nil 和 Max 值,每次最多 500 个。你可以选择小写、大写或花括号格式,去掉连字符,用换行、逗号、空格或 JSON 数组分隔列表,然后复制或下载为 .txt 或 .json 文件。生成 v3 和 v5 时,可选择 DNS、URL、OID 或 X.500 命名空间,也可以粘贴自己的命名空间。
内置校验器接受规范格式、花括号、urn:uuid: 和不带连字符的输入,并显示版本、变体和规范形式。它能解码 v1 和 v7 UUID 中的时间戳,以及 v1 的时钟序列和节点。几点限制:它不生成 v6 和 v8;v1 使用随机节点而不是你的 MAC 地址;同一毫秒内生成的 v7 ID 不保证按创建顺序排序,因为时间戳之后的位是随机数,而不是计数器。
常见问题
UUID 和 GUID 是一回事吗?
是的。GUID 是 Microsoft 对 UUID 的称呼。值和文本格式都相同;Microsoft 工具常用大写加花括号显示 GUID,部分 Microsoft API 在二进制形式中以小端字节序存储前三个字段。
两个 UUID 会重复吗?
理论上会,实际上几乎不会。对于 v4,需要约 2.71 × 10^18 个 UUID,出现一次重复的概率才达到 50%。真正的重复通常来自软件缺陷或损坏的随机数生成器,所以仍应在键列上保留唯一约束。
应该用 UUID v4 还是 v7?
插入频繁的表的主键用 v7,因为按时间排序的 ID 能让索引保持紧凑,并按创建时间排序。如果 ID 会公开,且不应透露记录的创建时间,就用 v4。
UUID 有多长?
UUID 是 128 位,即 16 个字节。标准文本形式为 36 个字符:32 个十六进制数字加 4 个连字符。去掉连字符是 32 个字符,加上花括号是 38 个。
在 URL 中使用 UUID 安全吗?
可以公开显示,但它不能代替访问控制。随机 v4 很难猜,但可能通过日志、浏览器历史记录或 Referer 请求头泄露,而 v1 和 v7 会暴露创建时间。务必检查用户是否有权访问该资源。
免费试用:UUID 生成器 免费使用,无需注册账号。