UUID 详解:全局唯一标识的原理与版本之争
UUID 解决什么问题
分布式系统里,多个节点同时生成 ID,怎么保证不重复?数据库自增主键靠中央计数器,跨节点就失效了。UUID(Universally Unique Identifier,通用唯一标识符)的思路是:让每个节点独立生成 ID,靠足够大的空间和好的生成算法,让冲突概率小到可以忽略。
UUID 是 128 位(16 字节)的标识符,标准格式是 32 个十六进制字符加 4 个连字符:
550e8400-e29b-41d4-a716-446655440000
128 位意味着 2¹²⁸ ≈ 3.4×10³⁸ 种可能,这个空间大到”随机碰撞几乎不可能”。但”几乎不可能”到底多不可能,取决于生成方式——这正是各版本 UUID 的区别所在。
各版本的生成原理
UUID 标准定义了多个版本,version 字段(第 3 段首位)标识用了哪种。理解每版的原理,才知道该用哪个。
v1:基于时间 + MAC 地址
- 原理:用当前时间戳(100 纳秒精度)+ 机器 MAC 地址生成
- 优点:有序(按时间递增)、可排序
- 缺点:暴露机器 MAC 地址(隐私问题)、依赖机器时钟(时钟回拨会出问题)、多线程同纳秒需协调
- 用途:需要时间有序且不介意暴露 MAC 的场景,现已少用
v2:DCE Security
少见,基于 v1 加 POSIX 用户/组 ID,基本没人用。
v3 / v5:基于命名空间 + 名称的哈希
- 原理:给定一个命名空间 UUID + 一个名称字符串,做哈希(v3 用 MD5,v5 用 SHA-1)生成
- 特性:确定性——同样的命名空间+名称永远生成同样的 UUID
- 区别:v3 用 MD5(已不推荐),v5 用 SHA-1,新场景用 v5
- 用途:需要”相同输入得相同 ID”的场景,如把 URL 映射成固定 UUID
v4:纯随机(最常用)
- 原理:122 位全随机(6 位是版本/变体标识),用 CSPRNG 生成
- 优点:简单、无依赖、不暴露任何信息、生成极快
- 缺点:无序(随机的),做数据库主键时索引碎片化严重
- 用途:绝大多数场景的默认选择
v6 / v7 / v8:新时代方案
v4 虽常用,但”无序”在数据库场景是硬伤——随机主键导致 B+ 树索引频繁页分裂、写入性能差。新标准(RFC 9562)引入:
- v7:时间戳(毫秒,48 位)+ 随机(74 位)。前半是时间,后半随机,所以整体按时间递增、可排序,又保留随机性避免冲突。这是当前最受推崇的新版本——既适合分布式生成,又适合做数据库主键(有序写入快)
- v6:v1 的重排版,把时间戳放前面变有序
- v8:自定义,留给厂商定义的变体
冲突概率:v4 真的不会撞吗
v4 有 122 位随机,冲突概率遵循”生日悖论”。粗略估算:要生成约 2⁶¹(约 2.3×10¹⁸)个 UUID 才有 50% 概率出现一次冲突。即使每秒生成 10 亿个,也要 70 年才到这个量级。
所以对任何现实应用,v4 冲突可以视为不可能——前提是用的是密码学安全的随机源(CSPRNG)。如果用普通 Math.random() 这种伪随机且种子可预测,理论安全性荡然无存,可能被猜出已生成的 UUID。生成 UUID 必须用 CSPRNG(浏览器 crypto.randomUUID()、Node crypto.randomUUID())。
选哪个版本
| 场景 | 推荐 |
|---|---|
| 通用唯一 ID、不关心顺序 | v4(默认) |
| 数据库主键、需有序写入 | v7(新项目)或 ULID |
| 需要确定性(同输入同输出) | v5 |
| 需要时间可追溯 | v7 或 v1(介意隐私则不用 v1) |
v4 是当下的安全默认,v7 是数据库主键的未来。如果项目能用新库,主键场景优先 v7。
UUID vs 自增 vs 雪花 ID
- 自增 ID:短、有序、紧凑,但需中央协调、暴露业务量、分库难
- UUID:去中心化、无冲突,但长(36 字符)、v4 无序
- 雪花算法(Snowflake):时间+机器ID+序列,有序且去中心化,但需分配机器 ID、依赖时钟
UUID 的优势是”零协调、开箱即用”,劣势是”长”。对存储/索引敏感的场景,可存为 16 字节二进制而非 36 字符字符串来节省空间。要更短的可读 ID,看 [[nanoid]] 和 [[ulid]]。
常见误区
- 把 UUID 当密码/令牌:v4 是随机的能用,但 UUID 设计目标是”唯一”不是”不可猜测”——有些实现随机性不够强。做 token 用专门的 CSPRNG 生成更稳妥
- 存成字符串浪费空间:36 字符 vs 16 字节二进制,海量数据下差别大,数据库可存 BINARY(16)
- v4 当主键嫌慢:随机主键索引碎片化是真问题,这种情况换 v7/ULID/雪花
- 以为所有 UUID 都随机:v1/v3/v5 不随机,v1 还暴露 MAC,别混淆
小结
UUID 是 128 位全局唯一标识,靠大空间+好算法让冲突可忽略。v4(纯随机)是当下默认,v7(时间+随机)因有序而成为数据库主键的新趋势,v5 适合确定性映射。选型核心看”要不要有序”:要有序选 v7,不要选 v4,要确定选 v5。无论哪版,随机性必须来自 CSPRNG。要更短或有序可读的替代方案,见 [[ulid]] 和 [[nanoid]]。