有许多系统依赖于某些特定值的唯一性。任何使用GUID的东西都会浮现在脑海中(例如,Windows注册表或其他数据库),还有从对象创建哈希以识别它的东西,因此需要这个哈希是唯一的。
哈希表通常不介意两个对象是否具有相同的哈希,因为哈希只是用于将对象分解为类别,因此在查找时,不是表中的所有对象,而只是表中的那些对象必须将相同类别(桶)的身份与搜索对象进行比较。
然而,其他实现(似乎)取决于唯一性。我的例子(这就是让我问这个的原因)是Mercurial的修订版ID。 Mercurial邮件列表上的entry正确显示
变更集散列的几率 在你的第一次意外碰撞 十亿次提交基本上为零。但 我们会注意到它是否会发生。和 你会成为那个有名的人 突然打破了SHA1。
但即使是最小的概率并不意味着不可能。现在,我不想解释为什么完全可以依赖于唯一性(例如,已经讨论过here)。这对我来说非常清楚。
相反,我想知道(也许是通过你自己的工作中的例子):
是否有关于涵盖这些不可能的案例的最佳做法?
是否应该忽略它们,因为特别强烈的太阳风更有可能导致硬盘读取错误?
他们至少应该接受测试,如果只是因为“我放弃了,你已经完成了不可能的”消息而失败了吗?
或者甚至应该优雅地处理这些案件?
对我来说,特别是下面的内容很有趣,虽然它们有点敏感:
如果你不处理这些案件,你会怎样对待那些不听概率的直觉?
如果你确实处理过这些问题,你如何证明这项工作(对你自己和其他人)是合理的,考虑到你有更多可能的案例你不能处理,比如超级新星?
答案 0 :(得分:7)
答案是你没有测试发现偶然发生的GUID冲突。您正在测试发现由于GUID代码中的错误而发生GUID冲突,或者GUID代码依赖于您违反(或被某些攻击者违反)的前提条件,例如在V1中MAC地址是独一无二的,时间在前进。要么比基于超新星的错误更有可能。
但是,并非GUID代码的每个客户端都应该测试其正确性,尤其是在生产代码中。这就是单元测试应该做的事情,所以要避免错过你的实际使用会遇到的但是单元测试没有的错误的成本,而不是一直在猜你的库的成本。
另请注意,GUID仅在生成它们的每个人合作时才有效。如果您的应用程序在您控制的计算机上生成ID,那么您可能不需要GUID - 像递增计数器这样的本地唯一ID可能对您没问题。显然Mercurial不能使用它,因此它使用哈希值,但最终SHA-1将陷入产生冲突的攻击(或者更糟糕的是,预映像),并且它们必须改变。
如果你的应用程序在你无法控制的机器上生成非哈希“GUID”,比如客户端,那么就会忘记意外冲突,你会担心恶意客户试图DOS服务器的故意冲突。无论如何,保护自己不会对事故造成伤害。
答案可能是“不”。如果你可以优雅地处理冲突的GUID,就像哈希表一样,那么为什么还要使用GUID呢? “标识符”的重点是,如果两个东西具有相同的ID,那么它们就是相同的。如果你不想对它们进行同样的处理,最初只是像哈希表一样将它们引导到桶中,然后使用不同的方案(如哈希)。
答案 1 :(得分:4)
鉴于良好的128位散列,在给定随机输入的情况下可能与特定散列值发生冲突:
1 / 2 ** 128
大约等于3 * 10 ** -39
。
可以使用用于解释birthday problem的逻辑来计算看到p
样本没有发生碰撞的概率(n
。
p = (2 ** 128)! / (2 ** (128 * n) * (2 ** 128 - n)!)
其中!
表示阶乘函数。然后,随着样本数量的增加,我们可以绘制无碰撞的概率:
在10**17
和10**18
哈希之间,我们开始看到碰撞的非平凡可能性从0.001%到0.14%,最后是10**19
哈希值的13%。因此,在一个拥有数百万亿的系统中,依赖于唯一性的记录可能是不明智的(并且这种系统是可以想象的),但在绝大多数系统中,碰撞的可能性非常小,以至于您可以依赖于哈希的唯一性出于所有实际目的。
现在,除了理论之外,更有可能通过错误或某人攻击你的系统将冲突引入你的系统,因此即使意外碰撞的可能性消失,onebyone的回答也提供了检查碰撞的充分理由。小的(也就是说,错误或恶意的概率远高于意外碰撞)。