您何时真正被迫使用UUID作为设计的一部分?


123

我真的不明白UUID的意义。我知道发生碰撞的可能性实际上nil,但实际上nil几乎不可能。

有人可以举一个例子,您除了使用UUID别无选择。从我所看到的所有用途中,我可以看到没有UUID的替代设计。当然,设计可能会稍微复杂一些,但至少它不会有非零的失败概率。

对我来说,UUID闻起来像全局变量。全局变量可以通过多种方式来简化设计,但这只是懒惰的设计。


23
一切都有非零的失败机会。与UUID的碰撞相比,我将更多地关注发生问题(即几乎所有您能想到的问题)的原因
DanSingerman 2009年

16
实际上,“实际上为零”几乎是不可能的。
mqp,2009年

21
不,它实际上无限地遥不可及
-Pyrolistical

32
@Pyrolistical当您开始抛出诸如“ infinity”之类的词时,您已经离开了软件开发领域。计算机科学理论是与编写实际软件完全不同的讨论。
Rex M

2
我之所以关闭,主要是因为git的sha1让我相信了哈希的好处
Pyrolistical 2011年

Answers:


617

我为Ruby编写了UUID生成器/解析器,因此我认为自己对这个主题有足够的了解。有四个主要的UUID版本:

从加密安全的随机数生成器提取的版本4 UUID本质上仅是16个字节的随机性,并且通过一些位纠缠来标识UUID版本和变体。这些冲突极不可能发生,但是如果使用PRNG或碰巧碰到真,真的,真的,真的,真的很不幸,就可能发生这种情况。

版本5和版本3 UUID分别使用SHA1和MD5哈希函数,以将名称空间与一段已经唯一的数据结合起来以生成UUID。例如,这将允许您从URL生成UUID。仅当基础哈希函数也有冲突时,才可能发生冲突。

版本1 UUID是最常见的。他们使用网卡的MAC地址(除非经过欺骗,否则应该是唯一的),时间戳记以及通常的比特级更改来生成UUID。如果机器没有MAC地址,则使用加密安全的随机数生成器生成6个节点字节。如果以足够快的顺序生成两个UUID,以使时间戳与先前的UUID相匹配,则时间戳将增加1。除非发生以下情况之一,否则不应发生冲突:MAC地址被欺骗;一台运行两个不同的UUID生成应用程序的机器在完全相同的时刻生成UUID。为两台没有网卡或没有用户级访问MAC地址的机器分配了相同的随机节点序列,并在同一时刻生成UUID。

实际上,所有这些事件都不会在单个应用程序的ID空间内偶然发生。除非您接受整个Internet范围内的ID,或者在不受信任的环境中接受恶意的ID冲突的情况,否则恶意个人可能会在ID冲突的情况下做一些不好的事情,除非您不必担心。至关重要的是要理解,在大多数情况下,如果您恰好生成与我相同的版本4 UUID,则没关系。我在与您完全不同的ID空间中生成了ID。我的应用程序永远不会知道该碰撞,因此该碰撞无关紧要。坦白说,在没有恶意行为者的单个应用程序空间中,即使在版本4 UUID上,即使在

另外,2 ^ 64 * 16是256艾字节。像这样,在单个应用程序空间中有50%的ID冲突机会之前,您需要存储256艾字节的ID。


8
到目前为止,这是最好的解释。我不知道为什么这没有被选为最高。Sporkmonger对您表示敬意。
布拉德·巴克

1
@Chamnap我写了UUIDTools。UUID可以转换为整数或它们的原始字节形式,并且将大大小于二进制。
Bob Aman 2012年

1
@Chamnap uuid.raw将为您提供字节字符串。该hash方法对您没有用。它用于Ruby内部的哈希表和比较操作。用于在各种UUID表示之间进行转换的所有方法均定义为类方法,并且应以开头"parse"
Bob Aman 2012年

3
@BobAman在1990年,我在一个Aegis系统上发生了12次UUID冲突,结果证明是FPU出现故障,但我想让您知道它可能会发生(除了最近30多年的编程之外没有发生过) 。很好的解释,顺便说一句,现在这是我给人们的事实上的UUID参考帖子:)
GMasucci 2014年

2
@kqr绝对正确,这是生日问题,但是对于n位代码,生日悖论问题减少到2 ^(n / 2),在这种情况下为2 ^ 64,如我的回答所述。
Bob Aman

69

UUID买给您的东西很难做到,否则您将获得唯一的标识符,而无需咨询或与中央机构协调。没有某种托管基础结构就可以得到这样的东西的普遍问题是UUID解决的问题。

我已经读到,根据生日悖论,一旦生成2 ^ 64个UUID,发生UUID冲突的机会是50%。现在2 ^ 64是一个相当大的数字,但是发生碰撞的几率高达50%似乎太冒险了(例如,在发生碰撞的几率达5%之前,需要存在多少个UUID-即使这似乎太大了) 。

该分析存在两个问题:

  1. UUID并不是完全随机的-UUID的主要组成部分是基于时间和/或位置的。因此,要想有真正的碰撞机会,需要从不同的UUID生成器中准确地同时生成冲突的UUID。我想说,虽然有可能同时生成多个UUID,但还有足够的其他问题(包括位置信息或随机位)使得几乎不可能在这套非常小的UUID之间发生冲突。

  2. 严格来说,UUID只需要在可能与之进行比较的其他UUID集合中是唯一的即可。如果要生成要用作数据库密钥的UUID,则在邪恶的替代宇宙中的其他地方使用相同的UUID来标识COM接口也没关系。就像如果在Alpha-Centauri上还有一个名叫“ Michael Burr”的人(或其他东西),这也不会引起混淆。


1
具体的例子?COM / DCE UUID-没有分配它们的权限,没有人想要承担责任和/或没有人希望拥有一个权限。没有可靠链接且没有主服务器的分布式数据库。
Michael Burr

3
更具体的例子-银行应用程序。它安装了多个数据中心,每个国家/地区都有一个,每个数据中心都有一个数据库。那里有多种安装方式供您遵循不同的法规。只能有在为每一位客户的整套一个客户记录.....
维尼特·雷诺兹

(前面的评论的继续)您需要有一台中央服务器来生成用于整体报告和跟踪目的的客户ID(在所有安装中),或者让单个安装生成用作用户ID的UUID(显然,UUID不能用于在报告中)。
Vineet Reynolds

到您有50%的重复机会时,您已经淹死了。有人指出达到0.0000001%机率所需的音量。多个从1到n开始并每次增加n的自动增量数据库有效地解决了同一问题。
戈登

2
复制的几率是FAR,远低于中央机构以某种关键任务方式失败的几率
std''OrgnlDave

33

一切都有非零的失败机会。与UUID的冲突相比,我将更多地关注发生问题(即几乎所有您能想到的问题)的可能性


添加为答复Pyrolistical的要求
DanSingerman

16

强调“合理地”或您所说的“有效地”:现实世界的工作方式就足够好。填补“实际上唯一”和“真正唯一”之间的空白所涉及的计算工作量很大。唯一性是收益递减的曲线。在那条曲线的某个点上,在仍然可以承受“足够独特”的地方之间存在一条线,然后我们非常陡峭地弯曲。增加更多唯一性的成本变得相当大。无限的唯一性具有无限的代价。

相对而言,UUID / GUID是一种生成ID的计算快速简便的方法,可以合理地认为该ID 是通用的。这在许多需要集成以前未连接的系统中的数据的系统中非常重要。例如:如果您有一个在两个不同平台上运行的内容管理系统,但是在某个时候需要将内容从一个系统导入另一个系统。您不希望ID发生更改,因此您在系统A中的数据之间的引用保持不变,但是您不希望与系统B中创建的数据发生任何冲突。UUID可以解决此问题。


解。不要偷懒并更新参考。做对了。
Pyrolistical

8
这与懒惰无关-如果策略是将某个项目的ID视为永久且不可变,则该ID不会更改。因此,您希望ID从一开始就具有唯一性,而您无需从一开始就以某种方式连接所有系统就可以做到这一点。
Michael Burr

然后,您需要上下文。如果您有可能会冲突的两组唯一ID,则需要高水平的上下文来将它们分开
Pyrolistical 2009年

23
或者,您可以只构建使用UUID的系统,然后发货,出售,赚取一百万美元,却再也不会听到两个ID冲突的投诉,因为这不会发生。
Rex M

16

创建UUID绝对不是绝对必要的。但是,有一种方便的标准,即脱机用户可以生成冲突可能性极低的密钥。

这可以帮助数据库复制解析等。

在线用户很容易为某事生成唯一密钥,而不会产生开销或冲突的可能性,但这不是UUID的目的。

无论如何,从维基百科摘录的有关碰撞可能性的词:

从这些数字来看,据估计,一个人每年被陨石击中的风险是170亿的机会,这相当于在一年内创造数十万亿个UUID并重复一次的可能性。换句话说,只有在接下来的100年中每秒生成10亿个UUID之后,才创建一个副本的可能性约为50%。


4
简单,不要让离线用户生成密钥。在系统联机之前分配临时密钥,以便可以生成真实密钥。
Pyrolistical

在我看来,这是一个非常有用的答案……我想对自己的可能性提供某种类比,因为OP似乎不太了解其含义,但您似乎已经做到了。
Noldorin

我安静地了解概率实际上为零。对我来说,UUID的使用是一种懒惰的设计,我只想看看您是否总是可以避免使用它
Pyrolistical 2009年

只要您看到在最极端的情况下甚至需要考虑低概率,就足够了,就像我现在假设的那样。
Noldorin

13

一个经典的例子是在两个数据库之间复制时。

DB(A)插入一个ID为10的记录,同时DB(B)创建一个ID为10的记录。这是冲突。

对于UUID,这将不会发生,因为它们将不匹配。(几乎可以确定)


1
好的,然后让DB A使用偶数ID,DB B使用奇数ID。完成,没有UUID。
Pyrolistical

2
对于三个DB,请使用3

20
如果您使用2/3 /任意倍数,那么以后再添加新服务器时会发生什么?您必须协调开关,以便在新服务器上使用n + 1倍数,并将所有旧服务器移至新算法,并且在执行此操作时必须关闭所有服务器,以避免在运行期间发生冲突算法开关。或者...您可以只使用每个人都可以使用的UUID。
Bob Aman

3
甚至比这更糟,因为您如何区分2的倍数和4的倍数?还是3的倍数与6的倍数?实际上,您必须坚持质数的倍数。死神!只需使用UUID,它就可以工作。微软,苹果和无数其他人都依靠他们并信任他们。
sidewinderguy 2013年

2
@sidewinderguy,我们信任GUID!:)
罗恩·克莱因

13

还有一个非零的可能性,即您体内的每个粒子都会同时穿过您坐在的椅子,然后突然发现自己坐在地板上。

你担心吗?


7
当然不是,那不是我可以控制的,但是我可以设计。
Pyrolistical

4
@Pyrolistical是真的,我说的是真的,你不用担心什么原因呢?那你真奇怪。而且,你是不对的。您可以控制它。如果您增加了几磅,则可以大大降低发生此类事件的可能性。那您认为应该增加体重吗?:-)
Veky

8

我有一个避免UUID的方案。在某处设置服务器并拥有它,以便每当某个软件想要通用的唯一标识符时,他们都会与该服务器联系,然后服务器将其分发出去。简单!

除了存在一些实际的实际问题外,即使我们忽略了彻底的恶意。特别是,该服务器可能会从Internet的一部分发生故障或无法访问。处理服务器故障需要复制,而且很难做到这一点(请参阅Paxos算法的文献,以了解共识构建的原因)并且速度也很慢。此外,如果所有的服务器都从“网的特定部分不可达,连接到子网的客户端将能够做任何事情,因为他们都会等待着新的ID。

因此,...使用简单的概率算法来生成它们,在地球的生命周期内不太可能发生故障,或者(拨款并)建立将成为部署PITA并经常发生故障的主要基础架构。我知道我要去哪一个。


2
实际上,UUID发明的全部目的是避免采用您的方法。如果您研究UUID的历史,您会发现它源于创建复杂而有意义的计算机网络的最早实验。他们知道网络本质上是不可靠和复杂的。当您知道计算机之间无法持续通信时,UUID回答了如何在计算机之间协调数据的问题。
罗勒·布尔克

7
@BasilBourque我在第一段中使用讽刺,以防万一。
Donal Fellows 2013年

5

我还没有完全谈到碰撞的可能性。我不在乎碰撞。我在乎性能。

https://dba.stackexchange.com/a/119129/33649

对于非常大的表,UUID会对性能造成损害。(200K行不是“很大”。)

当CHARCTER SET为utf8时,您的#3确实很糟糕-CHAR(36)占用108个字节!

UUID(GUID)非常“随机”。在大型表上将它们用作UNIQUE或PRIMARY键非常低效。这是因为每次插入新的UUID或通过UUID进行SELECT时,都必须在表/索引周围跳转。当表/索引太大而无法放入高速缓存(请参阅innodb_buffer_pool_size,该值必须小于RAM,通常为70%)时,“下一个” UUID可能不会被高速缓存,因此磁盘命中速度很慢。当表/索引的大小是高速缓存的20倍时,仅高速缓存1/2/5(5%)的命中数-您受I / O约束。

因此,除非任何一个都不要使用UUID

您有“小”表,或者由于从不同位置生成唯一ID(并且还没有想出另一种方法)而真正需要它们。有关UUID的更多信息:http : //mysql.rjweb.org/doc.php/uuid(它包括用于在标准36个字符的UUID和BINARY(16)之间转换的函数。)

在同一表中同时拥有UNIQUE AUTO_INCREMENT和UNIQUE UUID都是浪费。

发生INSERT时,必须检查所有唯一/主键是否重复。这两个唯一密钥都足以满足InnoDB拥有PRIMARY KEY的要求。BINARY(16)(16个字节)有点笨重(反对将其设为PK),但还不错。当您具有辅助键时,体积很重要。InnoDB默默地将PK附加到每个辅助密钥的末尾。这里的主要课程是最大程度地减少辅助键的数量,尤其是对于非常大的表。为了进行比较:INT UNSIGNED是4个字节,范围为0..4十亿。BIGINT是8个字节。


4

如果仅查看简单数据库应用程序的替代方法,而不必每次创建新对象之前都要查询数据库,那么您很快就会发现使用UUID可以有效地降低系统的复杂性。授予-如果您使用int密钥,则它们是32位,将存储在128位UUID的四分之一中。理所当然-UUID生成算法比简单地增加一个数字占用更多的计算能力。但谁在乎?根据指定的唯一性ID空间,管理“权限”以分配其他唯一号的开销很容易超过数量级。


3

在UUID ==懒惰的设计

我不同意它与您打架。如果从统计学上讲不可能有重复的UUID,并且已经证明了数学原理,那么为什么还要担心?花时间在小型N UUID生成系统周围进行设计是不切实际的,总有许多其他方法可以改善系统。


1

在我的上一份工作中,我们从第三方获得了以UUID唯一标识的对象。我放入了UUID-> long integer查找表,并使用long integer作为我的主键,因为这样做的速度更快。


可以肯定,第三方强迫您使用UUID是我不想讨论的另一个问题。假设您可以控制是否使用UUID。
Pyrolistical

好吧,“长整数”(128位)实际上就是UUID。它只是显示为供人食用的字符串。有时可能以这种方式进行传输,但是对于存储和索引,它肯定会以您发现的整数形式更快。
妮可

1

使用版本1算法,在相同的MAC地址每毫秒生成少于10个UUID的约束下,似乎不可能发生冲突

从概念上讲,UUID的原始(版本1)生成方案是将UUID版本与生成UUID的计算机的MAC地址以及自西方采用格里高利历以来的100纳秒间隔连接起来。实际上,实际算法更加复杂。有人批评这种方案“不够透明”。它既揭示了生成UUID的计算机的身份,又揭示了生成UUID的时间。

如果我误解了它的工作原理,请有人纠正我


版本很多,许多软件系统(例如Java)不能使用版本1,因为它不是纯Java方式来访问mac地址。
Pyrolistical

关于Java无法获取MAC地址:并非完全正确。有解决方法。您可以通过配置文件手动设置生成器使用的MAC地址。您还可以调出ifconfig并解析输出。我编写的Ruby UUID生成器使用两种方法。
Bob Aman

另外,如我的回答中所述,如果您无法获得版本1 UUID的MAC地址,则根据RFC 4122第4.5节,使用6个随机字节代替。因此,即使您不想使用其中一个这两种针对Java的解决方法,您仍然可以生成有效的版本1 UUID。
Bob Aman

MS GUID只是随机数。它们不再具有任何MAC部分,因为这使得对服务器的MAC地址进行反向工程成为可能(事实证明这是非常危险的)。
Stefan Steiger,2010年

1

对于那些说UUID不好的设计,因为它们可能会碰撞(以极小概率),而数据库生成的密钥不会...您知道由于某些原因,人为错误会导致数据库生成的密钥发生冲突预见到的需求是FAR FAR远高于UUID4碰撞的机会。我们知道,如果重新创建数据库,它将再次从1开始创建id,并且当我们确定永远不需要时,我们当中有多少人不得不重新创建表?每天都有未知的未知物出现问题时,我会把钱花在UUID安全上。


0

除了必须使用需要UUID的其他人的API的情况以外,当然总会有另一种解决方案。但是这些替代方案能否解决UUID 所解决的所有问题?当您一次可以解决所有这些问题时,您是否最终会增加更多的hacks层,每层都可以解决一个不同的问题?

是的,UUID在理论上可能会发生冲突。正如其他人指出的那样,它根本不值得考虑,这很荒谬。迄今为止从未发生过,很可能永远不会发生。忘掉它。

避免冲突的最“明显”方法是让单个服务器在每个插入上生成唯一的ID,这显然会造成严重的性能问题,并且根本无法解决离线生成问题。哎呀

另一种“显而易见的”解决方案是中央机构,该机构预先分发唯一编号的块,这实际上是UUID V1通过使用生成机的MAC地址(通过IEEE OUI)执行的操作。但是确实会出现重复的MAC地址,因为每个中央机构最终都会搞砸,因此在实践中,这比UUID V4冲突更有可能。哎呀

反对使用UUID的最佳论点是它们“太大”,但是(显着)较小的方案将不可避免地无法解决最有趣的问题。UUID的大小是其在解决这些问题时有用的内在副作用。

您的问题可能还不够大,无法使用UUID提供的功能,在这种情况下,请随意使用其他功能。但是,如果您的问题出乎意料地增长(并且大多数情况都如此),您最终将在以后切换-踢自己一开始就不要使用它们。为什么要为失败而设计却又为成功而设计一样容易?


-10

UUID体现了与全局变量相关的所有不良编码实践,但更糟糕的是,因为它们是可以分布在不同套件中的超全局变量。

最近遇到了这样的问题,那就是用确切的替换型号替换打印机,并且发现没有任何客户端软件可以工作。


2
很高兴我们生活在一个仍然关注事实而不是随机意见的社会中,否则我们所有人都陷入困境。:)
马卡兰德'17
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.