131

我真的不明白UUID的意义。我知道碰撞的概率实际上是 nil,但实际上 nil甚至不是不可能的。

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

UUID 对我来说闻起来像全局变量。全局变量有很多方法可以简化设计,但它只是懒惰的设计。

4

16 回答 16

651

我为 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,或者在不受信任的环境中,恶意个人可能会在 ID 冲突的情况下做坏事,否则您不必担心。至关重要的是,如果您碰巧生成了与我相同的版本 4 UUID,在大多数情况下,这并不重要。我在与您完全不同的 ID 空间中生成了 ID。我的应用程序永远不会知道碰撞,所以碰撞无关紧要。坦率地说,在没有恶意行为者的单个应用程序空间中,地球上所有生命的灭绝将在您发生碰撞之前很久就发生,即使在版本 4 UUID 上,即使您

此外,2^64 * 16 是 256 艾字节。例如,您需要存储 256 EB 的 ID,然后才有 50% 的机会在单个应用程序空间中发生 ID 冲突。

于 2009-04-24T16:11:01.850 回答
73

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”时不会引起混淆一样。

于 2009-03-31T21:49:14.420 回答
35

任何事情都有非零失败的机会。我会专注于更可能发生的问题(即几乎任何你能想到的问题)而不是 UUID 的冲突

于 2009-04-03T13:35:11.740 回答
17

强调“合理地”,或者,正如你所说的,“有效地”:现实世界的运作方式已经足够好。弥补“几乎独一无二”和“真正独一无二”之间的差距所涉及的计算工作量是巨大的。唯一性是一条收益递减的曲线。在这条曲线上的某个点,在“足够独特”仍然可以负担得起的地方之间有一条线,然后我们的曲线非常陡峭。添加更多独特性的成本变得相当大。无限的独特性具有无限的成本。

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

于 2009-03-31T21:06:18.160 回答
16

创建 UUID 从来都不是绝对必要的。然而,有一个标准是很方便的,在这个标准中,离线用户每个人都可以生成一个碰撞概率非常低的东西的密钥。

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

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

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

从这些数字来看,一个人每年被陨石击中的风险估计为 170 亿分之一,相当于在一年内创建几十万亿个 UUID 并有一个重复的几率。换句话说,仅在接下来的 100 年每秒生成 10 亿个 UUID 之后,仅创建一个副本的概率约为 50%。

于 2009-03-31T21:09:13.707 回答
15

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

你担心吗?

于 2009-03-31T22:32:23.990 回答
13

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

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

使用 UUID 不会发生这种情况,因为它们不会匹配。(几乎可以确定)

于 2009-03-31T21:05:03.480 回答
9

我有一个避免 UUID 的方案。在某处设置一个服务器并拥有它,这样每当某个软件需要一个通用唯一标识符时,他们就会联系该服务器并分发一个。简单的!

除了这有一些真正的实际问题,即使我们忽略了彻底的恶意。特别是,该服务器可能会出现故障或无法从部分 Internet 访问。处理服务器故障需要复制,而且很难做到正确(请参阅有关 Paxos 算法的文献,了解为什么建立共识很尴尬),而且速度也很慢。此外,如果从网络的特定部分无法访问所有服务器,则连接到该子网的所有客户端无法执行任何操作,因为它们都将等待新的 ID。

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

于 2010-04-07T09:04:23.690 回答
6

我没有得到所有关于碰撞可能性的讨论。我不在乎碰撞。不过我关心性能。

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

UUID 对于非常大的表来说是一场性能灾难。(200K 行不是“非常大”。)

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

UUID (GUID) 非常“随机”。将它们用作大型表上的唯一键或主键是非常低效的。这是因为每次插入新的 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 字符 UUID 和 BINARY(16) 之间转换的函数。)

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

发生 INSERT 时,必须检查所有唯一/主键是否重复。任一唯一键都足以满足 InnoDB 拥有主键的要求。BINARY(16)(16 字节)有点庞大(反对将其设为 PK 的论据),但还不错。当您有辅助键时,体积很重要。InnoDB 默默地将 PK 附加到每个辅助键的末尾。这里的主要教训是最小化辅助键的数量,尤其是对于非常大的表。比较:INT UNSIGNED 为 4 字节,范围为 0..4 亿。BIGINT 为 8 个字节。

于 2016-09-08T06:26:16.740 回答
4

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

于 2010-01-26T15:47:14.163 回答
3

关于 UUID==惰性设计

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

于 2009-03-31T21:52:06.737 回答
1

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

于 2009-03-31T21:04:22.797 回答
1

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

从概念上讲,UUID 的原始(版本 1)生成方案是将 UUID 版本与生成 UUID 的计算机的 MAC 地址以及自西方采用公历以来的 100 纳秒间隔数连接起来. 在实践中,实际算法更复杂。该方案被批评为不够“不透明”;它揭示了生成 UUID 的计算机的身份以及它生成的时间。

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

于 2009-03-31T21:23:58.477 回答
1

对于那些说 UUID 是糟糕的设计的人,因为它们可能(以某种可笑的小概率)发生冲突,而您的数据库生成的密钥不会……您知道人为错误的可能性会导致您的数据库生成的密钥发生冲突,因为一些 un - 预见需要远高于 UUID4 冲突的机会。我们知道,如果重新创建数据库,它将再次从 1 开始 ids,当我们确定永远不需要重新创建表时,我们中有多少人不得不重新创建表?当任何一天未知的未知事物开始出现问题时,我会把钱花在 UUID 安全上。

于 2015-12-04T21:24:02.460 回答
1

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

是的,理论上 UUID 有可能发生冲突。正如其他人所指出的那样,它不太可能达到不值得考虑的程度。迄今为止从未发生过,而且很可能永远不会发生。忘掉它。

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

另一个“显而易见”的解决方案是一个中央机构,它提前分发唯一编号块,这本质上是 UUID V1 通过使用生成机器的 MAC 地址(通过 IEEE OUI)所做的。但是重复的 MAC 地址确实会发生,因为每个中央机构最终都会搞砸,所以在实践中,这比 UUID V4 冲突更有可能发生。哎呀。

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

您的问题可能不够大,不足以需要 UUID 提供的功能,在这种情况下,请随意使用其他东西。但是,如果您的问题出乎意料地增长(而且大多数情况如此),那么您最终会在以后切换 - 并且会因为一开始没有使用它们而自责。当为成功而设计同样容易时,为什么要为失败而设计呢?

于 2018-07-31T22:24:13.423 回答
-11

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

最近遇到这样的问题,用一个确切的替换型号替换打印机,发现没有一个客户端软件可以工作。

于 2012-10-12T07:14:00.323 回答