我从 docs. 了解两者之间的区别
uuid1()
:
根据主机 ID、序列号和当前时间生成 UUID
uuid4()
:
生成随机 UUID。
因此,uuid1
使用机器/序列/时间信息来生成 UUID。使用每种方法的优缺点是什么?
我知道 uuid1()
可能存在隐私问题,因为它基于机器信息。我想知道在选择其中一个时是否有更微妙的地方。我现在只使用 uuid4()
,因为它是一个完全随机的 UUID。但我想知道是否应该使用 uuid1
来降低碰撞风险。
基本上,我正在寻找人们关于使用一个与另一个的最佳实践的提示。谢谢!
uuid1()
保证不会产生任何冲突(假设您不会同时创建太多冲突)。如果 uuid
和计算机之间没有连接很重要,我不会使用它,因为 mac 地址被用来使其在计算机之间唯一。
您可以通过在 100ns 内创建超过 214 个 uuid1 来创建副本,但这对于大多数用例来说都不是问题。
如您所说,uuid4()
生成一个随机 UUID。发生碰撞的机会真的非常非常非常小。足够小,你不应该担心它。问题是,一个糟糕的随机数生成器使它更有可能发生冲突。
This excellent answer by Bob Aman 总结得很好。 (我建议阅读整个答案。)
坦率地说,在没有恶意行为者的单个应用程序空间中,地球上所有生命的灭绝将在您发生碰撞之前很久就发生,即使在版本 4 UUID 上,即使您每秒生成相当多的 UUID。
您可以考虑使用 uuid1()
而不是 uuid4()
的一个实例是在不同的机器上生成 UUID,例如,在多台机器上处理多个在线事务以实现扩展目的时。
在这种情况下,例如,由于伪随机数生成器的初始化方式选择不当而导致发生冲突的风险,并且产生的 UUID 数量可能更高,从而更有可能创建重复 ID。
uuid1()
的另一个兴趣点是,在这种情况下,最初生成每个 GUID 的机器被隐式记录(在 UUID 的“节点”部分中)。如果仅用于调试,此信息和时间信息可能会有所帮助。
我的团队刚刚在使用 UUID1 进行数据库升级脚本时遇到了麻烦,我们在几分钟内生成了大约 12 万个 UUID。 UUID 冲突导致违反主键约束。
我们已经升级了 100 台服务器,但在我们的 Amazon EC2 实例上我们遇到了几次这个问题。我怀疑时钟分辨率不佳并切换到 UUID4 为我们解决了这个问题。
使用 uuid1
时要注意的一件事,如果您使用默认调用(不提供 clock_seq
参数),您有可能遇到冲突:您只有 14 位随机性(在 100ns 内生成 18 个条目给您大约 1发生碰撞的百分比见生日悖论/攻击)。在大多数用例中,这个问题永远不会发生,但在时钟分辨率较差的虚拟机上,它会咬你一口。
clock_seq
的良好实践示例非常有用......
也许没有提到的是地方性。
MAC 地址或基于时间的排序 (UUID1) 可以提高数据库性能,因为与随机分布的数字 (UUID4) 相比,将数字排序得更近的工作量更少(请参阅 here)。
第二个相关问题是,即使原始数据丢失或未明确存储,使用 UUID1 在调试中也很有用(这显然与 OP 提到的隐私问题相冲突)。
除了接受的答案之外,还有第三个选项在某些情况下可能有用:
带有随机 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 对象;如果有人愿意,可以发布)
在每秒调用量很大的情况下,这有可能耗尽系统随机性。您可以改用 stdlib random
模块(它可能也会更快)。但请注意:攻击者只需几百个 UUID 就可以确定 RNG 状态,从而部分预测未来的 UUID。
import random
from uuid import uuid1
def uuid1mc_insecure():
return uuid1(random.getrandbits(48) | 0x010000000000)
不定期副业成功案例分享
uuid1
不一定会生成唯一的 UUID。示例:[uuid.uuid1() for i in range(2)]
。当然,除非发生了我想念的奇怪事情。uuid1
有一个序列号(您的示例中的第 4 个元素),因此除非您用完计数器中的所有位,否则您不会发生任何冲突。