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


207

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

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

uuid4()
生成随机UUID。

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

我知道uuid1()可能会涉及到隐私问题,因为它基于机器信息。我想知道在选择一个或另一个时是否还有其他细微之处。我uuid4()现在就使用,因为它是完全随机的UUID。但是我想知道我是否应该使用它uuid1来减少碰撞的风险。

基本上,我正在寻找人们使用某一种方法与另一种方法的最佳使用技巧。谢谢!


3
这是UUID的替代方法。尽管发生碰撞的机会是无穷的,但UUID不能保证唯一性。为了保证唯一性,您可能需要将复合键用作[<系统ID>,<本地ID>]。每个参与数据共享的系统都必须具有自己的系统唯一ID,该ID可以在系统设置过程中分配,也可以从通用ID池中获得。本地ID是任何特定系统内的唯一ID。这涉及更多麻烦,但保证了唯一性。对不起,请尝试帮助。
2013年

3
不照顾他提到的“隐私问题”
Shrey

Answers:


253

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

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

uuid4()如您所说,生成一个随机的UUID。碰撞的机会确实很小。足够小,您不必担心。问题在于,不良的随机数生成器使其更有可能发生冲突。

鲍勃·阿曼的出色回答很好地总结了这一点。(我建议阅读整个答案。)

坦白说,在没有恶意行为者的单个应用程序空间中,即使在每秒生成大量UUID的情况下,即使在版本4 UUID发生碰撞之前,地球上所有生命的消亡也会发生很久。


抱歉,我未进行充分研究就评论了-保留了一些阻止版本4 uuid与版本1 uuid冲突的功能。我将删除我的原始评论。参见tools.ietf.org/html/rfc4122
Mark Ransom,

1
@gs是的,我读的书很有道理。uuid1是“更独特的”,而uuid4是更匿名的。因此,除非您有理由不这样做,否则基本上使用uuid1。@mark赎金:很棒的答案,当我搜索uuid1 / uuid4时没有出现。似乎是从马口中直接出来的。
火箭猴

6
uuid1如果您在同一节点上每秒产生多个UUID,则不一定会产生唯一的UUID。范例:[uuid.uuid1() for i in range(2)]。除非发生某种奇怪的事情,否则我肯定会丢失。
Michael Mior

1
@Michael:uuid1具有序列号(示例中的第4个元素),因此,除非您用完计数器中的所有位,否则不会发生冲突。
GeorgSchölly13年

3
@Michael:我尝试研究发生碰撞的情况,并添加了我发现的信息。
GeorgSchölly13年

32

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

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

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


20

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

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


5

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


7
@Guilaume,看到一个使用clock_seq... 的良好做法的示例将非常有用。–
eric

@Guilaume您如何计算1%的机会?14位随机性意味着,如果您每100ns生成> = 2 ^ 14个id,则碰撞一定会发生;这意味着,当您每100 ns产生大约163 id时,发生碰撞的几率是1%
maks

1
@maks如我所说,您应该看一下生日悖论
纪尧姆

3

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

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

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


1

除了可接受的答案外,还有在某些情况下有用的第三种选择:

带有随机MAC的v1(“ 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)

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


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

import random
from uuid import uuid1

def uuid1mc_insecure():
    return uuid1(random.getrandbits(48) | 0x010000000000)

好像这种方法“像” v4(与主机无关),但更糟(位少,对urandom的依赖性等)。与uuid4相比,有什么优势吗?
rocketmonkeys

这主要是针对v1对其基于时间的质量有用但又需要更强的抗碰撞性和主机隐私性的情况的升级。一个示例是作为数据库的主键-与v4相比,v1 uuid在写入磁盘时将具有更好的局部性,具有更有用的自然排序等。但是,如果您遇到攻击者预测2 **的情况, 61位是一个安全性问题(例如,以uuid作为随机数),然后$ diety是,请改用uuid4(我知道我这样做!)。回复:更糟糕的是,因为它使用了urandom,所以我不确定您的意思-在python中,uuid4()也使用urandom。
伊莱·柯林斯

好东西,这是有道理的。很高兴看到不仅是您可以做的(您的代码),而且还想知道为什么要这么做。回复:urandom,我的意思是您要消耗2倍的随机性(uuid1为1,urandom为1),因此可以更快地耗尽系统熵。
火箭猴'17

实际上,它大约是uuid4的一半:uuid1()将14位用于clock_seq,它舍入为2个字节的urandom。uuid1mc包装器使用48位,应映射到urandom的6个字节,每次调用总共消耗urandom(8)。而uuid4会为每个调用直接调用urandom(16)。
伊莱·柯林斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.