243

我从文档中了解两者之间的区别。

uuid1()
根据主机 ID、序列号和当前时间生成 UUID

uuid4()
生成一个随机的 UUID。

因此uuid1使用机器/序列/时间信息来生成 UUID。使用每种方法的优缺点是什么?

我知道uuid1()可能会有隐私问题,因为它是基于机器信息的。我想知道在选择其中一个时是否有更微妙的地方。我uuid4()现在就使用,因为它是一个完全随机的 UUID。但我想知道我是否应该使用它uuid1来减少碰撞的风险。

基本上,我正在寻找人们关于使用一个与另一个的最佳实践的提示。谢谢!

4

6 回答 6

282

uuid1()保证不会产生任何碰撞(假设您不会同时创建太多碰撞)。如果 Mac 和计算机之间没有连接很重要,我不会使用它uuid,因为 mac 地址被用来使其在计算机之间具有唯一性。

您可以通过在 100ns 内创建超过 2个 14 uuid1 来创建副本,但这对于大多数用例来说不是问题。

uuid4()正如你所说,生成一个随机的 UUID。发生碰撞的可能性真的非常非常小。足够小,你不应该担心它。问题是,一个糟糕的随机数生成器使它更有可能发生冲突。

Bob Aman 的这个出色的回答很好地总结了这一点。(我建议阅读整个答案。)

坦率地说,在没有恶意行为者的单个应用程序空间中,地球上所有生命的灭绝将在您发生碰撞之前很久就发生,即使在版本 4 UUID 上,即使您每秒生成相当多的 UUID。

于 2009-11-23T20:05:41.577 回答
37

您可能会考虑的一种情况是在不同的机器上生成 UUID 时uuid1(),例如在多台机器上处理多个在线事务以用于扩展目的时。uuid4()

在这种情况下,例如,由于伪随机数生成器的初始化方式选择不当而导致发生冲突的风险,并且产生的 UUID 数量可能更高,从而更有可能创建重复 ID。

在这种情况下,另一个兴趣uuid1()是隐式记录最初生成每个 GUID 的机器(在 UUID 的“节点”部分中)。如果仅用于调试,此信息和时间信息可能会有所帮助。

于 2009-11-23T20:18:20.850 回答
33

我的团队刚刚在使用 UUID1 进行数据库升级脚本时遇到了麻烦,我们在几分钟内生成了大约 12 万个 UUID。UUID 冲突导致违反主键约束。

我们已经升级了 100 台服务器,但在我们的 Amazon EC2 实例上我们遇到了几次这个问题。我怀疑时钟分辨率不佳并切换到 UUID4 为我们解决了这个问题。

于 2015-12-21T08:51:22.250 回答
7

使用时要注意的一件事uuid1,如果您使用默认调用(不提供clock_seq参数),您有可能遇到冲突:您只有 14 位随机性(在 100ns 内生成 18 个条目,您大约有 1% 的发生冲突的机会见生日悖论/攻击)。在大多数用例中,这个问题永远不会发生,但在时钟分辨率较差的虚拟机上,它会咬你一口。

于 2014-06-17T18:49:50.797 回答
6

也许没有提到的是地方性。

MAC 地址或基于时间的排序 (UUID1) 可以提高数据库性能,因为与随机分布的数字 (UUID4) 相比,将数字排序得更近的工作量更少(参见此处)。

第二个相关问题是,即使原始数据丢失或未明确存储,使用 UUID1 在调试中也很有用(这显然与 OP 提到的隐私问题相冲突)。

于 2017-04-13T16:46:30.617 回答
3

除了接受的答案之外,还有第三个选项在某些情况下可能有用:

带有随机 MAC 的 v1(“v1mc”)

您可以通过故意生成具有随机广播 MAC 地址的 v1 UUID 来混合 v1 和 v4(这是 v1 规范允许的)。生成的 v1 UUID 是时间相关的(如常规 v1),但缺少所有主机特定信息(如 v4)。它的抗碰撞性也更接近 v4:v1mc = 60 位时间 + 61 个随机位 = 121 个唯一位;v4 = 122 个随机位。

我遇到的第一个地方是 Postgres 的uuid_generate_v1mc()函数。从那以后,我使用了以下 python 等效项:

from os import urandom
from uuid import uuid1
_int_from_bytes = int.from_bytes  # py3 only

def uuid1mc():
    # NOTE: The constant here is required by the UUIDv1 spec...
    return uuid1(_int_from_bytes(urandom(6), "big") | 0x010000000000)

(注意:我有一个更长、更快的版本,可以直接创建 UUID 对象;如果有人愿意,可以发布)


在每秒调用量很大的情况下,这有可能耗尽系统随机性。您可以改用 stdlibrandom模块(它可能也会更快)。但请注意:攻击者只需几百个 UUID 就可以确定 RNG 状态,从而部分预测未来的 UUID。

import random
from uuid import uuid1

def uuid1mc_insecure():
    return uuid1(random.getrandbits(48) | 0x010000000000)
于 2017-08-18T18:09:08.277 回答