你什么时候真的被迫使用UUID作为设计的一部分?

时间:2009-03-31 21:01:38

标签: architecture uuid

我真的没有看到UUID的重点。我知道碰撞的可能性实际上是零,但实际上没有甚至几乎不可能。

有人可以举例说明你别无选择,只能使用UUID吗?从我见过的所有用途中,我可以看到没有UUID的替代设计。当然,设计可能稍微复杂一点,但至少它没有非零的失败概率。

UUID闻起来像是全局变量。全局变量有许多方法可以实现更简单的设计,但它只是懒惰的设计。

16 个答案:

答案 0 :(得分:575)

我为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空间内偶然发生。除非您在互联网范围内接受身份证,或者在恶意个人可能在ID冲突的情况下做坏事的不受信任环境,否则您不应该担心。至关重要的是要理解,如果您碰巧生成与我相同的版本4 UUID,在大多数情况下,它并不重要。我在与您完全不同的ID空间中生成了ID。我的应用程序永远不会知道碰撞,因此碰撞无关紧要。坦率地说,在没有恶意攻击者的单个应用程序空间中,即使在版本4 UUID上,即使你每秒生成相当多的UUID,地球上所有生命的灭绝也会在你发生碰撞之前很久就会消失。

此外,2 ^ 64 * 16是256艾字节。与此同时,在单个应用程序空间中发生ID冲突的可能性为50%之前,您需要存储256 EB的ID。

答案 1 :(得分:67)

UUID给你买的非常难以做到的事情就是获得一个唯一的标识符,而不必咨询或协调中央权威。在没有某种托管基础设施的情况下能够获得这样的事情的一般问题是UUID解决的问题。

我已经读过,根据生日悖论,一旦生成了2 ^ 64个UUID,UUID发生碰撞的可能性为50%。现在2 ^ 64是一个相当大的数字,但是50%的碰撞几率似乎太冒险了(例如,在碰撞机率为5%之前需要存在多少UUID - 即使这看起来有太大的概率)

该分析的问题有两个:

  1. UUID并非完全随机 - UUID的主要组件是基于时间和/或位置的。因此,为了在碰撞中有任何真正的机会,冲突的UUID需要在不同的UUID生成器的同时生成。我会说,虽然有可能同时生成几个UUID,但是还有足够的其他gunk(包括位置信息或随机位)来使这个非常小的UUID集之间的冲突几乎不可能

  2. 严格来说,UUID只需要在可能与之对比的其他UUID集中是唯一的。如果您要生成UUID以用作数据库密钥,那么在邪恶的备用Universe中的其他位置使用相同的UUID来标识COM接口并不重要。就像在Alpha-Centauri上有一个名叫“Michael Burr”的人(或其他东西)一样,它不会引起混淆。

答案 2 :(得分:29)

一切都有非零的失败机会。我会更专注于比UUID碰撞更容易发生问题(即几乎你能想到的任何事情)

答案 3 :(得分:16)

强调“合理地”或者正如你所说的那样,“有效地”:现实世界的运作方式就足够了。覆盖“实际上独特”和“真正独特”之间差距所涉及的计算工作量是巨大的。唯一性是一种收益递减的曲线。在该曲线的某个点上,在“足够独特”仍然可以承受的位置之间存在一条线,然后我们曲线非常陡峭。增加更多唯一性的成本变得非常大。无限的独特性具有无限的成本。

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

答案 4 :(得分:14)

永远不一定要创建UUID。然而,有一个标准是方便的,其中离线用户每个都可以生成一个碰撞概率非常低的东西的密钥。

这可以帮助解决数据库复制等问题......

在线用户很容易为没有开销或可能发生冲突的事情生成唯一的密钥,但这不是UUID的用途。

无论如何,关于碰撞概率的一个词,取自维基百科:

  

将这些数字放在一个视角中,一个人每年被击中的风险   据估计,陨石是一个机会,相当于170亿   在一年内创造几十万亿UUID的可能性   有一个重复。换句话说,只有在产生10亿之后   UUIDs在接下来的100年中每秒都有创造的概率   只有一个重复约50%。

答案 5 :(得分:12)

你的身体中的每个粒子都会同时穿过你正坐在椅子上的一个非零概率,你会突然发现自己正坐在地板上。

你担心吗?

答案 6 :(得分:11)

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

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

使用UUID时,这不会发生,因为它们不匹配。 (几乎可以肯定)

答案 7 :(得分:7)

我有一个避免UUID的方案。在某个地方设置服务器并使其具有每次某个软件需要通用唯一标识符时,它们会联系该服务器并将其移出。简单!

除非有一些实际的实际问题,即使我们忽视了彻头彻尾的恶意。特别是,该服务器可能会失败或无法访问部分互联网。处理服务器故障需要复制,并且非常困难才能正确完成(请参阅Paxos算法的文献,了解为什么建立共识很困难)并且速度也很慢。此外,如果所有服务器都无法从“网络”的特定部分访问,则连接到该子网的客户端的 none 将能够执行任何操作,因为它们都将等待新的ID。 / p>

所以...使用一个简单的概率算法来生成它们,这些算法在地球生命周期中不太可能失败,或者(资金和)构建一个主要的基础设施,它将成为部署PITA并经常出现故障。我知道我要去哪一个。

答案 8 :(得分:4)

如果你只是看看替代品,例如对于一个简单的数据库应用程序,每次在创建新对象之前都必须查询数据库,您很快就会发现使用UUID可以有效地降低系统的复杂性。当然 - 如果你使用int键是32位,它将存储在128位UUID的四分之一。当然 - UUID生成算法比简单地递增数字需要更多的计算能力。但谁在乎?管理“权限”以分配其他唯一数字的开销很容易超过数量级,具体取决于您的预期唯一性ID空间。

答案 9 :(得分:3)

关于UUID ==懒惰设计

我不同意接受你的战斗。如果一个重复的UUID在统计上是不可能的并且数学证明了那么为什么要担心?花费时间围绕您的小型N UUID生成系统进行设计是不切实际的,总有十几种其他方法可以改进您的系统。

答案 10 :(得分:2)

我不会谈论碰撞的可能性。我不在乎碰撞。我关心表现。

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

  

UUID是非常大的表的性能灾难。 (200K行是   不是“非常大”。)

     

当CHARCTER SET为utf8时你的#3非常糟糕 - CHAR(36)   占用108个字节!

     

UUID(GUID)非常“随机”。将它们用作UNIQUE或   大型表的主键非常低效。这是因为   每次插入新的UUID时都必须跳转表/索引   或者由UUID选择。当表/索引太大而无法放入缓存时   (参见innodb_buffer_pool_size,它必须小于RAM,   通常为70%),“下一个”UUID可能不会被缓存,因此磁盘速度较慢   击中。当表/索引是缓存的20倍时,只有1/20   (5%)命中被缓存 - 你是I / O绑定的。

     

因此,除非

,否则不要使用UUID      

你有“小”表,或者你真的需要它们因为生成   来自不同地方的独特ID(并没有找到另一种方式   去做吧)。有关UUID的更多信息:http://mysql.rjweb.org/doc.php/uuid(它   包括在标准的36-char UUID和。之间进行转换的功能   BINARY(16)。)

     

同时具有UNIQUE AUTO_INCREMENT和UNIQUE UUID   桌子是浪费。

     

发生INSERT时,必须检查所有唯一/主键   重复。唯一键对于InnoDB的要求就足够了   有一个主要的关键。 BINARY(16)(16字节)有点笨重(一个   反对把它变成PK的论点,但不是那么糟糕。庞大   当你有二级钥匙时很重要。 InnoDB默默地解决了PK问题   到每个二级密钥的末尾。这里的主要教训是   最小化辅助键的数量,特别是对于非常大的   表。比较:INT UNSIGNED为4个字节,范围为0..4   十亿。 BIGINT是8个字节。

答案 11 :(得分:1)

在我上一份工作中,我们从第三方获取了与UUID唯一标识的对象。我输入了一个UUID->长整数查找表,并使用长整数作为我的主键,因为它的速度更快。

答案 12 :(得分:1)

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

  

从概念上讲,原作(版本1)   UUID的生成方案是   将UUID版本连接到   计算机的MAC地址   生成UUID,并与   数量为100纳秒的间隔   自格列高利人采用以来   在西方的日历。在实践中,   实际算法比较复杂。   这个计划受到了批评   它不够“不透明”;   它揭示了两者的身份   生成UUID和的计算机   这样做的时间。

如果我误解了它的运作方式,有人会纠正我

答案 13 :(得分:1)

对于那些说UUID设计不好的人,因为他们可以(以一些可笑的小概率)发生碰撞,而你的数据库生成的密钥却赢了......你知道人为错误的可能性由于一些不确定的需求导致数据库生成的密钥发生冲突,因此远远超过UUID4冲突的可能性。我们知道如果重新创建数据库,它将再次启动1,并且当我们确定永远不需要时,我们有多少人必须重新创建表?当任何事情开始出现错误时,我会把我的钱投入到UUID的安全性上。

答案 14 :(得分:0)

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

是的,从理论上讲,UUID可能会发生冲突。正如其他人指出的那样,这简直是不可能的,因为它根本不值得考虑。迄今为止从未发生过,很可能永远不会发生。算了吧。

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

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

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

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

答案 15 :(得分:-10)

UUID体现了与全局变量相关的所有错误编码实践,更糟糕的是,因为它们是超全局变量,可以分布在不同的工具包上。

最近出现了更换具有精确替换型号的打印机的问题,并发现没有一个客户端软件能够正常工作。