GUID冲突可能吗?

156
我正在处理一个在SQL Server 2000中使用GUID的数据库,每个使用此应用程序的用户都有一个GUID。不知怎么的,两个用户的GUID相同了。我知道Microsoft使用算法生成随机GUID,几乎不可能发生冲突,但是仍然有可能发生冲突吗?

14
每个说“不”的人都是错的。我已经将一个唯一标识符与少于50万条记录的数据集发生了冲突,使用的是MSSQL 2008 R2。 - Behrooz
4
哎呀。由于生日悖论,这并不是不可能的,但对于完全随机的v4 GUID来说仍然是极为不幸的。也许你正在使用更弱的GUID生成策略? - Craig Ringer
7
哇,这运气真是太出乎意料了。 - Craig Ringer
9
这可能是在 MSSQL 中使用的有缺陷的伪随机数(鉴于其软件质量,我不会惊讶他们的生成器中有 32 位种子或类似的内容)。数学是不会撒谎的。这种可能性非常小,以至于您可以确信 99.9999999999% (后面跟着很多个9),MSSQL 的 GUID 生成器有缺陷(或者可能是用来生成 GUID 的伪随机数生成器),或者您犯了一个错误。 - Alex
5
此时此刻,问题和被选答案的分数都是128分,真巧合吗? - Caio Cunha
显示剩余9条评论
20个回答

5

首先声明,“我不是网络工程师,因此我可能会说出完全不连贯的句子。”

在伊利诺伊州立大学工作时,我们有两台戴尔台式电脑,分别在不同的时间订购。我们将第一台连接到了网络上,但当我们尝试将第二台连接到网络时,我们开始收到一些奇怪的错误信息。经过长时间的故障排除发现,两台机器都生成了相同的GUID(我不确定确切的用途,但它使它们无法在网络上使用)。最终,Dell公司替换了这两台电脑。


3
具体来说是GUID的问题,与机器加入网络时生成的GUID有关。Dell花了几周时间更换机器,因为他们认为GUID不可能相同。我们能够重现这个问题,Dell拿回了机器,并在他们的网络上产生了相同的结果。最终他们替换了两台机器。就像我之前提到的,我不是网络专业人员,但我特别记得这是GUID的问题。 - John Kraft

5

通用公式

有一个公式可以估算生成大小为S的值,以使得两个值之间发生碰撞的概率为P。

变量:

  • bits - 数据类型中包含的位数。
  • probability - 碰撞的目标概率。

要发生碰撞,你需要生成约:

2^{\frac{bits + 1}{2}} * \sqrt{-log_2(1 - probability)}

或者在Python中:

from math import sqrt, log

def how_many(bits, probability):
    return 2 ** ((bits + 1) / 2) * sqrt(-log(1 - probability))

GUID(全局唯一标识符)

对于长度为128位的GUID来说,要使碰撞概率达到1%(0.01),需要:

In [2]: how_many(bits=128, probability=0.01)
Out[2]: 2.6153210405530885e+18

… 大约有 2.6 x 10^18 个GUID(即 42艾字节 的GUID)。

请注意,这个概率增长得非常快。无论位数如何,对于99.99%的概率,您只需要比1%多30倍的GUID!

In [3]: how_many(bits=128, probability=0.9999)
Out[3]: 7.91721721556706e+19

Int64

相同的数字,但用于int64数据类型:

In [4]: how_many(bits=64, probability=0.01)
Out[4]: 608926881

In [5]: how_many(bits=64, probability=0.9999)
Out[5]: 18433707802

如果要达到1%的碰撞概率,你需要5GB的int64-s。虽然仍然很多,但与GUID相比,这是一个更容易理解的数字。


这就是所谓的生日悖论问题 - 在这篇维基百科文章中,你可以找到比这更精确的估算公式。


3

生成GUID的代码可能存在漏洞吗?当然可能。但是,与编译器的错误一样,你自己的代码更有可能存在错误,因此首先要检查自己的代码。


2

当然这是可能的……但很不可能发生。

请记住,同一台机器生成每个GUID(服务器),因此基于特定机器信息的“随机性”大部分都会丢失。


1

只是为了好玩,试试下面的脚本...(适用于SQL 2005,不确定2000是否可行)

declare @table table
(
    column1 uniqueidentifier default (newid()),
    column2 int,
    column3 datetime default (getdate())
)

declare @counter int

set @counter = 1

while @counter <= 10000
begin
    insert into @table (column2) values (@counter)
    set @counter = @counter + 1
end

select * from @table

select * from @table t1 join @table t2 on t1.column1 = t2.column1 and t1.column2 != t2.column2

重复运行此操作(少于一秒钟)会从第一个选择中产生相当广泛的范围,即使时间间隔极短。到目前为止,第二个选择还没有产生任何结果。


1
你需要在计数器末尾再加上15个零,才有50%的概率出现重复。但是,求你别这么做! - Jim Birchall

0

如果您是通过 SQL Server 中的 NEWID() 函数生成 GUID,那么遇到 GUID 冲突的可能性非常小(当然,其他答案已经强调了这一点,但仍有可能发生)。他们没有指出的一件事是,如果您在浏览器中使用 JavaScript 生成 GUID,则很可能会遇到冲突。不仅不同浏览器中的 RNG 有时存在问题,而且我还遇到过 Google 爬虫似乎缓存了此类函数的结果,并且反复将相同的 GUID 传递给我们的系统。

有关更多详细信息,请参见此处的各种答案:

在 JavaScript 中生成 UUID 时是否会发生冲突?


0

如果用户使用不同的机器和网络卡,那么这是不可能的,即使没有也只是极其边缘的理论风险。

个人认为更有可能是一个错误而不是GUID冲突,建议您在其他地方寻找解决方案...

当然,前提是您不会截断GUID以使其变短。


1
GUID将在服务器上生成,因此用户的网络卡不会发挥作用。 - Tom Ritter

0

不要担心它是什么。让它变得不可能。将GUID的不可信性与顺序的不可能性混合在一起。只需将数据库顺序ID添加到GUID中,然后称之为完成。您可能需要将数据类型从GUID更改为类似字符串的类型,但在存储方面它们并没有太大的区别。


0
我和我的同事讨论了这个问题,以下是我们得出的结论:
一个GUID有10的38次方个唯一值,要达到50%的碰撞概率,需要10的19次方个元素才能发生一次GUID碰撞(假设每个GUID都是随机的),这将需要:
每秒产生一千个随机GUID的一百万个线程,持续一百万年(大约)。
所以,从理论上讲,是的,GUIDs并不是唯一的。
但是从实际上来看,要出现重复的概率非常低。

-1

当然是可能的,甚至可能性很高。这并不像每个GUID都在可能的数字空间的随机部分中一样。如果两个线程同时尝试生成一个GUID,在没有某种带有信号量的集中式GUID函数的情况下,它们可能会得到相同的值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接