UUID 指南:v4 与 v7 的区别,以及用 UUID 作数据库主键
550e8400-e29b-41d4-a716-446655440000 这样的字符串随处可见,用来标识订单、文件和 API 请求。了解 UUID 是如何生成的,以及该选择哪个版本。
什么是 UUID
UUID(通用唯一识别码)是一个 128 位的标识符,任何人无需询问中央服务器就能生成不会冲突的 ID。微软称之为 GUID。现行标准是 2024 年发布的 RFC 9562。
它写成 32 个十六进制数字,用连字符分成 8-4-4-4-12 五组,共 36 个字符。第三组的第一个数字表示版本:xxxxxxxx-xxxx-4xxx-... 是 v4,7xxx 是 v7。
各版本一览
目前通常的选择是:一般用途用 v4,像数据库主键这样重视排序和插入性能的场景用 v7。
- v1:结合时间戳和机器的 MAC 地址。按时间排序,但可能泄露硬件信息。
- v3 和 v5:对命名空间和名称做哈希,相同输入总是得到相同的 UUID(v3 用 MD5,v5 用 SHA-1)。
- v4:122 位随机数。使用最广泛,实现也最简单。
- v7:48 位毫秒级 Unix 时间戳后接随机位。按创建顺序排序。
UUID 真的不会重复吗?
v4 UUID 的随机部分有 122 位,约有 5.3 × 10^36 种可能值。按生日问题计算,要让出现一次重复的概率达到 50%,大约需要 2.7 × 10^18 个 UUID——相当于每秒生成 10 亿个,持续 85 年。实际上不会发生碰撞。
前提是随机数真正随机。曾有报告称,在随机数生成器状态相同的情况下克隆虚拟机,或使用可预测随机函数的自制实现时出现了重复。请使用平台的标准 UUID 函数。
UUID 作为主键
UUID 主键可以在多台服务器上同时生成,也不会暴露记录数量。如果使用自增整数,/orders/1052 这样的 URL 就会让竞争对手推测出你的订单量。
代价在于大小和顺序。UUID 占 16 字节,是 8 字节整数的两倍,若存为 36 个字符的字符串则更大。由于 v4 完全随机,新行会落在 B 树索引的任意位置,导致频繁的页分裂和写入变慢。v7 随时间递增,基本可以避免这个问题。
- PostgreSQL:使用原生 uuid 类型(16 字节)。
- MySQL:存为 BINARY(16);8.0 及以上版本提供 UUID_TO_BIN。
- SQL Server:使用 uniqueidentifier 类型。
何时不应把 UUID 当作密钥
UUID 的设计目标是唯一的名称,而不是无法猜测的秘密。v1 和 v7 包含时间戳,因此部分位是可预测的。密码重置令牌、API 密钥等安全敏感的值,应由足够多的密码学安全随机字节生成。
用 AIBIZAP UUID 生成器按需批量生成 UUID,密钥则使用客户端凭据生成器。如果要根据 JSON 样例设计数据表,可以配合 JSON DDL 生成器一起使用。