UUID 指南:版本、格式与最佳实践
UUID(通用唯一标识符)是 128 位标识符,设计为在空间和时间上唯一,无需中央权威机构。它们是分布式系统的标准选择,在这些系统中,独立生成 ID — 无需协调 — 至关重要。
UUID 格式
UUID 是一个 128 位数字,以 32 个十六进制数字显示,分为五组,用连字符分隔:
550e8400-e29b-41d4-a716-446655440000
^^^^^^^^ ^^^^ ^^^^ ^^^^ ^^^^^^^^^^^^
time-low mid hi clk node
+ver +var
- 总计:128 位(16 字节)
- 字符串长度:36 个字符(32 个十六进制 + 4 个连字符)
- 版本:编码在第 13 个字符("hi" 半字节)中
- 变体:编码在第 17 个字符中
使用我们的 UUID 生成器 即时生成 UUID。
UUID 版本
UUID v1:基于时间
由当前时间戳和机器的 MAC 地址生成。
// 结构:时间戳(60 位)+ 时钟序列(14 位)+ 节点(48 位)
6ba7b810-9dad-11d1-80b4-00c04fd430c8
优点:按创建时间自然排序,每台机器保证唯一。 缺点:暴露 MAC 地址(隐私问题),可能泄露创建时间。 使用场景:需要时间排序且在受控环境中时。
UUID v4:随机
由加密安全随机数生成。这是使用最广泛的版本。
// 122 个随机位(6 位保留给版本和变体)
f47ac10b-58cc-4372-a567-0e02b2c3d479
优点:简单,无信息泄露,无需协调。 缺点:不可排序,数据库索引性能略低于顺序 ID。 使用场景:通用目的 — 这是大多数应用的默认选择。
UUID v7:时间排序随机(新标准)
最新版本(RFC 9562,2024),结合 Unix 时间戳和随机数据。
// 结构:时间戳(48 位)+ 随机(74 位)
018e7b50-4a00-7000-8000-000000000001
优点:自然按时间排序,出色的数据库索引性能,除毫秒精度外无信息泄露。 缺点:较新,某些库可能尚不支持。 使用场景:需要按时间排序的唯一 ID — 非常适合数据库主键。
其他版本
- UUID v3:命名空间和名称的 MD5 哈希。确定性的。
- UUID v5:命名空间和名称的 SHA-1 哈希。确定性的,优于 v3。
- UUID v6:重新排序的 v1,以获得更好的可排序性。已被 v7 取代。
- Nil UUID:全零(
00000000-0000-0000-0000-000000000000)。用作哨兵值。
碰撞概率
随机生成的 UUID(v4)碰撞的可能性有多大?有 122 个随机位:
- 生成 10 亿个 UUID 后:概率 ≈ 10^18 分之一
- 达到 50% 碰撞概率:需要大约 2.7 × 10^18 个 UUID
- 以每秒 10 亿个 UUID 的速度:大约需要 86 年
出于所有实际目的,UUID v4 碰撞不会发生。你遇到硬件故障的可能性要大得多。
UUID 与其他 ID 格式对比
| 格式 | 长度 | 可排序 | 唯一 | URL 安全 |
|---|---|---|---|---|
| UUID v4 | 36 字符 | 否 | 是 | 是 |
| UUID v7 | 36 字符 | 是 | 是 | 是 |
| ULID | 26 字符 | 是 | 是 | 是 |
| nanoid | 21 字符 | 否 | 大概率 | 是 |
| 自增 | 可变 | 是 | 单表内 | 是 |
| Snowflake ID | 18-19 字符 | 是 | 单系统内 | 是 |
ULID(通用唯一字典序可排序标识符)
ULID 是一种紧凑的替代方案:48 位时间戳 + 80 位随机数,编码为 26 个 Crockford Base32 字符。它们按创建时间进行字典序排序。
何时使用自增 ID
顺序 ID 对于单数据库应用更简单、更高效。在以下情况使用 UUID:
- 需要多个系统独立生成 ID
- 不希望向外部用户暴露插入顺序
- 分布式数据库需要可合并的 ID
数据库性能考虑
随机 UUID(v4)导致 B 树索引碎片化,因为插入发生在随机位置。在大表上,这可能使写入性能降低 2-5 倍。
解决方案:
- 使用 UUID v7:时间排序的 UUID 按顺序插入,匹配自增性能
- 以二进制存储:使用
BINARY(16)而不是CHAR(36)可节省 55% 的存储空间 - 使用 ULID:可排序且比 UUID 更紧凑
-- PostgreSQL:原生 UUID 类型(16 字节,高效)
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL
);
-- MySQL:以 BINARY(16) 存储以提高性能
CREATE TABLE users (
id BINARY(16) PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
生成 UUID
命令行
# macOS/Linux
uuidgen
# Python 单行命令
python3 -c "import uuid; print(uuid.uuid4())"
JavaScript
// 内置(Node.js 19+,现代浏览器)
crypto.randomUUID();
// UUID v7(使用 uuid 包)
import { v7 } from 'uuid';
const id = v7();
Python
import uuid
uuid.uuid4() # 随机
uuid.uuid5(uuid.NAMESPACE_DNS, 'example.com') # 基于名称
如需无需编码快速生成,我们的 UUID 生成器 可在浏览器中即时创建 v4 UUID。
常见问题
我应该使用 UUID 还是自增整数作为数据库主键?
对于单数据库应用,自增整数更简单、更快速。对于分布式系统、微服务,或者需要在数据库插入之前在客户端生成 ID 的情况,UUID(最好是 v7)是更好的选择。如果你使用 PostgreSQL,原生 UUID 支持使性能差异可以忽略不计。
UUID 可以用作安全令牌吗?
UUID v4 使用来自加密源的 122 位随机数,提供良好的熵。然而,对于安全令牌(API 密钥、会话 ID),最好使用具有附加属性的专用令牌格式,如校验和、过期编码或前缀标识。UUID 用于身份标识,而非认证。
相关资源
- UUID 生成器 — 在线即时生成 UUID
- 哈希算法对比 — 了解 UUID v3 和 v5 背后的哈希函数
- JSON API 设计模式 — 在 API 响应中有效使用 UUID