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

Pyr*_*cal 116 architecture uuid

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

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

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

Bob*_*man 588

我为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上,即使你每秒生成相当多的UUID,地球上所有生命的灭绝也会在你发生碰撞之前很久就会发生.

此外,2 ^ 64*16是256艾字节.同样,在单个应用程序空间中有50%的机会发生ID冲突之前,您需要存储256 EB的ID.

  • 这是迄今为止最好的解释.我不知道为什么这不被投票到顶部.感谢Sporkmonger. (8认同)
  • @BobAman在1990年我在Aegis系统上发生了12次UUID碰撞,原来是一个有缺陷的FPU,但我想我会告诉你它可能会发生(除了在过去30多年的编程中没有发生过) .很好的解释,太顺便说一句,这是现在我的事实UUID refence帖子给人:) (3认同)
  • @Chamnap 我写了 UUIDTools。UUID 可以转换为整数或其原始字节形式,并且作为二进制文件会小得多。 (2认同)
  • @eric.frederich 如果发生这种情况,请告诉我。 (2认同)
  • @GMasucci 好点。如果硬件坏了,或者如果有人决定 `/dev/random` 应该只返回 4,那么所有的赌注都会被取消。 (2认同)
  • @kqr你是完全正确的,这是生日问题,但是对于n位代码,生日悖论问题减少到2 ^(n/2),在这种情况下是2 ^ 64,如我的回答中所述. (2认同)

Mic*_*urr 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"的人(或其他东西)一样,它不会引起混淆.

  • 更具体的例子 - 银行应用程序.它安装了多个数据中心,每个国家一个,每个数据中心都有一个数据库.多个装置用于遵守不同的规定.对于每个客户,整个集合中只能有一个客户记录..... (3认同)
  • 获得副本的几率是FAR,比中央机构以某种关键任务方式失败的几率低 (2认同)

Dan*_*man 31

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


Rex*_*x M 16

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

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

  • 或者,您可以构建系统以使用UUID并运送它,出售它,赚取一百万美元并且从不听到两个ID相互冲突的单一投诉,因为它不会发生. (23认同)
  • 这与懒惰无关 - 如果策略是项目的ID被认为是永久性且不可变的,则ID不会更改.因此,您希望ID从一开始就是唯一的,并且您希望这样做而不需要从一开始就以某种方式连接所有系统. (8认同)

Joh*_*zen 15

创建UUID绝不是绝对必要的.然而,有一个标准是方便的,其中离线用户可以各自生成具有非常低的碰撞概率的东西的密钥.

这有助于数据库复制解决等...

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

无论如何,从维基百科中得到一个关于碰撞概率的词:

为了正确看待这些数字,人们每年被陨石击中的风险估计为170亿的一次机会,相当于一年内创造数十万亿UUID并且有一个重复的可能性.换句话说,只有在接下来的100年中每秒产生10亿UUID之后,创建一个副本的概率大约为50%.

  • 简单,不要让离线用户生成密钥.在系统上线之前分配临时密钥,以便生成真正的密钥. (3认同)

use*_*714 12

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

你担心吗?

  • 当然不是,这不是我能控制的东西,而是我能做的设计. (7认同)
  • @Pyrolistical是_that_真的,我的意思是你真的不担心的原因吗?那你很奇怪 而且,你不对.你_can_控制它.如果你增加几磅,你就会大大减少这种事件发生的可能性.你认为你应该增加体重吗?:-) (3认同)

Joh*_*lan 11

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

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

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

  • 如果您使用2/3 /任意倍数,以后在混合中添加新服务器时会发生什么?您必须协调一个开关,以便在新服务器上使用n + 1倍数,并将所有旧服务器移到新算法上,并且在执行此操作时必须关闭所有内容以避免冲突期间算法开关.或者......你可以像每个人一样使用UUID. (19认同)
  • 它甚至比那更糟糕,因为你如何区分2的倍数和4的倍数?或3的倍数与6的倍数?事实上,你必须坚持多个素数.布莱什!只需使用UUID即可.微软,苹果和其他无数人依赖它们并信任它们. (3认同)
  • 对于三个DB,请使用3倍大声笑 (2认同)
  • @sidewinderguy,我们相信GUID!:) (2认同)

Don*_*ows 7

我有一个避免UUID的计划.地方设置一台服务器,并将它让每一位了一个软件想要一个通用唯一标识符时,他们联系该服务器,并将其一只手出来.简单!

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

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

  • @BasilBourque我在第一段中使用了讽刺,以防它不明显. (6认同)
  • 实际上,UUIDs发明的全部意义在于避免你的方法.如果你研究UUID的历史,你会发现它来自最早创建复杂而有意义的计算机网络的实验.他们知道网络本质上是不可靠和复杂的.UUID回答了当您知道无法进行持续通信时如何在计算机之间协调数据的问题. (2认同)

Tos*_*kan 5

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

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

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

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

UUID (GUID) 非常“随机”。在大表上将它们用作 UNIQUE 或 PRIMARY 键是非常低效的。这是因为每次插入新的 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 对 PRIMARY KEY 的要求。BINARY(16)(16 字节)有点庞大(反对将其作为 PK 的论点),但还不错。当您有辅助键时,体积很重要。InnoDB 默默地将 PK 附加到每个辅助键的末尾。这里的主要教训是尽量减少辅助键的数量,特别是对于非常大的表。作为比较:INT UNSIGNED 是 4 个字节,范围为 4 亿。BIGINT 是 8 个字节。