每个 UUID 背后的问题
一个 UUID 有 128 位,通常随机生成(版本 4),从而任何地方的任何人都可在无需协调的情况下铸造一个,并期望它是唯一的。合理的担忧是:如果人人都只是掷骰子,难道两个不会最终重合吗?答案在原则上是肯定的,而在实践中几乎从不,数学恰好说明了原因。
真正有多少随机位
一个版本 4 的 UUID 并没有 128 个随机位。四位被固定以标记版本,另两位被固定以标记变体,从而剩下 122 个随机位。这仍是一个巨大的空间:2¹²²,约 5.3 × 10³⁶ 个可能值。
应用生日界
碰撞概率并不取决于填满该空间;它取决于配对的数量,而后者随你生成数量的平方增长。这又是生日悖论。对一个 2¹²² 个值的空间,n 个 UUID 之间出现任何碰撞的概率约为:
p ≈ n² / (2 × 2¹²²)
单次碰撞的 50% 概率只有在约 2⁶¹ 个 UUID 之后才会出现,约为 2.3 × 10¹⁸。具体而言:若以每秒十亿个版本 4 UUID 的速度生成,你需要约 85 年 的量级才能仅仅达到一次碰撞的 50% 概率。在任何现实的应用体量下,该概率小到被一次硬件故障无论如何会损坏数据的几率所掩盖。
唯一真正的告诫是随机性的质量。只有当生成器是一个恰当的、密码学上安全的来源时,该数学才成立。一个弱的或初始化不良的随机来源可能远更早地产生重复,因此实际风险是一个糟糕的随机数生成器,而非 UUID 的设计。
当不需要随机性时
有时担忧的不是碰撞,而是可重复性:
- 命名空间 UUID(版本 3 与 5) 是确定性的:它们对一个命名空间加一个名称进行哈希(v3 用 ,v5 用 -1),从而相同的输入总是产生相同的 UUID。当你想要一个从既有数据派生的稳定标识符、而非一个新的随机标识符时很有用。
- 时间排序 UUID(版本 7) 保留随机性,但在其前面加上一个时间戳,从而 ID 按创建时间排序。这有助于数据库中的索引局部性,同时碰撞风险仍可忽略。
教训
对任何正常规模的唯一标识符而言,来自一个良好随机来源的版本 4 UUID 不会碰撞,且你无需一个中央机构来保证这一点。当你需要相同输入映射到相同 ID 时选择 v3/v5,当你想要时间排序时选择 v7。
UUID 工具 生成版本 4(及其他)UUID,并解析任何 UUID 以显示其版本与变体,全部在你的浏览器中,不发送任何内容。