我什么时候应该在python中使用uuid.uuid1()和uuid.uuid4()?

roc*_*eys 197 python uuid

我从文档中了解了两者之间的差异.

uuid1():
从主机ID,序列号和当前时间生成UUID

uuid4():
生成随机UUID.

因此uuid1使用机器/序列/时间信息来生成UUID.使用每个的利弊是什么?

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

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

Geo*_*lly 243

uuid1()保证不会产生任何碰撞(假设您不会同时创建太多碰撞).如果重要的是它uuid和计算机之间没有连接,我就不会使用它,因为mac地址被用来使它在计算机上独一无二.

您可以通过在不到100ns 的时间内创建超过2 14 uuid1 来创建重复项,但对于大多数用例来说这不是问题.

uuid4()正如你所说,生成一个随机的UUID.碰撞的可能性确实非常小.足够小,你不应该担心它.问题是,一个坏的随机数生成器使它更容易发生冲突.

Bob Aman的这个出色的答案总结得很好.(我建议阅读整个答案.)

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

  • 如果在同一节点上每秒产生几个,则uuid1`不一定会生成唯一的UUID.示例:`[uuid.uuid1()for i in range(2)]`.除非当然发生了一些我不知道的奇怪事情. (6认同)
  • @Michael:我曾尝试研究碰撞发生时的情况,并添加了我发现的信息. (3认同)
  • @Michael:`uuid1` 有一个序列号(在你的例子中是第四个元素),所以除非你用完计数器中的所有位,否则你不会有任何冲突。 (2认同)

mjv*_*mjv 32

当你可以考虑一个实例uuid1(),而不是uuid4()当UUID的是在不同的机器生产的,例如,当多个网上交易是在几台机器换算目的的过程.

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

uuid1()在这种情况下,另一个兴趣是隐含地记录了最初生成每个GUID的机器(在UUID的"节点"部分中).这个和时间信息,如果只是调试,可能会有所帮助.


Mat*_*ren 16

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

我们升级了100台服务器,但在我们的Amazon EC2实例上,我们遇到过几次这个问题.我怀疑时钟分辨率很差,切换到UUID4为我们解决了它.


c z*_*c z 7

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

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

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


Eli*_*ins 6

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

v1 具有随机 MAC(“v1mc”)

您可以通过故意使用随机广播 MAC 地址生成 v1 UUID(这是 v1 规范允许的)来在 v1 和 v4 之间进行混合。生成的 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)
Run Code Online (Sandbox Code Playgroud)

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


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

import random
from uuid import uuid1

def uuid1mc_insecure():
    return uuid1(random.getrandbits(48) | 0x010000000000)
Run Code Online (Sandbox Code Playgroud)


小智 5

使用时需要注意的一点是uuid1,如果你使用默认调用(不给出clock_seq参数)你有可能遇到碰撞:你只有14位的随机性(在100ns内产生18个条目会让你有大约1%的碰撞几率看到生日悖论/攻击).在大多数用例中永远不会出现此问题,但在时钟分辨率较差的虚拟机上,它会咬你.

  • @Guilaume使用`clock_seq`看一个良好实践的例子真的很有用.... (7认同)