任何要存东西的系统都需要标识符,而默认选择的出错频率比人们以为的高。自增整数在合并两个数据库、把备份恢复进生产环境、或者接收第二个区域的写入时就会失效,因为序列已经发过一遍同样的数字了。让客户端自己生成标识符能解决这个,却引出另一个问题:客户端能控制的标识符,攻击者也能控制。随机标识符是通常的答案,UUID 是部署最广的形式,但这个缩写至少覆盖四种实质不同的东西。v4 是纯随机;v7 是时间有序的,只在中间带一点随机;v1 嵌入了时间戳和硬件地址;v5 由名字派生,所以每次都是同一个值。它们形状相同、互操作方式相同,选错要么让索引变慢,要么悄悄泄露你基础设施的信息。本页生成的是 v4,对绝大多数场景来说这正是正确答案。
v4、v7 和其他版本,以及该怎么选
现行规范是 2024 年发布的 RFC 9562,取代了更早的 RFC 4122。所有版本都是 128 位,写成 32 个十六进制字符加四段连字符,其中若干位被固定用来标识版本和变体,剩下 122 位可用。v4 把这 122 位全部填入随机数据。v7 是新规范里新增的,把 48 位大端毫秒时间戳放在前面,后面再跟随机位,于是取值天然按创建顺序排列。这一条差别对数据库很关键:随机主键会落在 B 树索引的任何位置,每次插入都造成页分裂和缓存局部性变差;时间有序的主键则是往末尾追加。v1 是旧的时间方案,嵌入了网卡硬件地址,既是隐私泄露,也让它以你名下某台机器的 MAC 地址而闻名。v3 和 v5 用 SHA-1 和 SHA-256 把名字连同命名空间一起哈希,同一个名字永远得到同一个值——适合做幂等键,但如果你要的是碰运气式的唯一性,它们毫无用处。默认用 v4;写入频繁的表可以考虑 v7。
碰撞概率到底有多大
v4 UUID 大约有 5.3 × 10 的 36 次方种可能取值,这就是人们引用的碰撞概率小到离谱的原因。用生日悖论算法算,生成十亿个随机 v4,出现任意一次碰撞的概率大约在 10 的负 19 次方量级,实际上等于永远不会。需要到百亿量级的标识符,50% 的临界点才具有可想象的概率。同样的推理也说明为什么小位宽的随机数行不通:32 位随机标识符在约 65000 个值之后就明显可能撞车,因为在巨大的取值空间里,碰撞来得比直觉早得多。两个实用提醒:有些运行时(包括 .NET)用不同的排布携带同样的 122 位,所以那边打印出来的值和这边打印出来的值保障相同但文本不同;以及,如果某个系统只保存了字符串的一部分,或者为了显示把它截断了,那么熵是真的损失了——被缩短的 UUID 应当当作另一个更弱的标识符来看待。
用 UUID 的代价在哪里
第一个常见抱怨是存储和索引,而且这个抱怨站得住:36 个字符的文本主键比 8 字节整数大得多,随机的插入顺序还会让索引碎片化。多数数据库都有原生的 UUID 类型,只占 16 字节,换掉文本列往往就是收益最大的一步改动。第二个是日志:每行 36 个随机字符让人读起来很痛苦,还会让日志聚合系统遇到高基数问题,所以请保留真正的 created_at 字段,不要试图从 v4 的值里倒推时间——里面没有时间。第三个是安全。v4 由密码学随机源生成,无法被猜出来,但 v1 的值会公开它何时何地被创建,而出现在 URL 里的 UUID 最后都会进日志和 referrer 头。如果某个 UUID 是你唯一的认证凭据,请顺便确认它是不是也躺在线上某条日志里。
常见问题
这些 UUID 真的是随机的吗?
是的,v4 的数值来自浏览器的密码学随机源,也就是你的平台用来生成密钥材料的那一个。它不是数学随机函数产生的,所以无法从之前的值反推,页面上出现的先后顺序也不包含任何关于数值本身的信息。
我该用 v4 还是 v7?
除非你的表写入非常频繁,否则用 v4。v7 把时间戳放在前面,新行会落到索引末尾而不是散落在各处,在繁忙的表上这个差别对写入性能相当明显。两者的不可猜测性相同,而 v7 已经是 UUID 主规范的一部分。
UUID 能当会话 token 或 API key 用吗?
v4 由强随机源生成,无法被猜知,用作链接或任务的标识符是合适的。要留意它经过的路径:任何出现在 URL 里的东西最后都会进服务端日志、浏览器历史和 referrer 头,所以既是密钥又是标识符的 UUID 就有泄露风险。v1 不适合任何敏感场景,因为它把时间戳和硬件地址都写进去了。
这个页面会发送任何东西吗?
不会。生成在浏览器标签页里完成,本页没有上传接口,关闭标签页数值就消失了。本站靠广告维持,广告服务商会看到页面层面的信息,比如你的 IP 地址和访问的页面,但看不到你生成的标识符。