在JavaScript中生成UUID时发生冲突?


94

这涉及到这个问题。我正在使用此答案中的以下代码在JavaScript中生成UUID:

'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
    return v.toString(16);
});

该解决方案似乎工作正常,但我遇到了冲突。这是我所拥有的:

  • 在Google Chrome中运行的网络应用。
  • 16个用户。
  • 这些用户在过去2个月中已生成约4000个UUID。
  • 我发生了大约20次碰撞-例如,今天生成的新UUID与大约2个月前相同(不同的用户)。

是什么导致此问题,如何避免呢?


2
将一个好的随机数与当前时间(以毫秒为单位)相结合。随机数恰好同时发生碰撞的几率非常低。
jfriend00'8

7
@ jfriend00如果您需要这样做,那么它不是一个“好的随机数”,甚至不是一个不错的伪随机数。
Attila O.

2
(r&0x3|0x8)部分是什么意思/评估?
克里斯蒂安2014年

追加一个Date.now()。toString()怎么样?
Vitim.us

4
您的架构中存在一个大问题,与UUID无关-客户端可能有意生成冲突ID。仅通过您信任的系统生成ID。但是,作为一种解决方法,请在客户端生成的ID前面添加user_id,以便对手/故障客户端只能与自己发生冲突(并在服务器端进行处理)。
Dzmitry Lazerka '16

Answers:


35

我最好的猜测是,Math.random()由于某种原因(听起来有点怪异),该系统已损坏。这是我见到的任何人发生碰撞的第一份报告。

node-uuid有一个测试工具,可用于测试该代码中十六进制数字的分布。如果这看起来还不错,那就不好了Math.random(),所以请尝试将您正在使用的UUID实现替换为uuid()那里的方法,看看您是否仍然获得了良好的结果。

[更新:刚看到Veselin关于Math.random()启动时的错误的报告。由于问题仅在启动时出现,因此该node-uuid测试不太可能有用。我将在devoluk.com链接上详细评论。]


1
谢谢,我现在要使用uuid.js,因为它使用了浏览器的强加密(如果可用)。会看看是否有任何碰撞。
Muxa

您能否提供指向您所引用的uuid.js代码的链接?(对不起,不确定您的意思是哪个库。)
broofa 2011年

10
到目前为止还没有发生碰撞:)
Muxa 2012年

无论如何,如果它是Chrome,并且只有在启动时,您的应用程序才能使用上述功能生成并丢弃一行,例如十个向导:)
Vinko Vrsalovic,2014年

问题在于您从Math.random()获得的熵有限。对于某些浏览器,熵加起来只有41位。多次调用Math.random()不会提高熵。如果您真的想要唯一的v4 UUID,则需要使用加密功能强的RNG,该RNG至少会为每个生成的UUID产生122位的熵。
mlehmk 2015年

36

确实存在冲突,但仅在Google Chrome浏览器下存在。在这里查看我在该主题上的经验

http://devoluk.com/google-chrome-math-random-issue.html

(链接从2019年开始断开。存档链接:https ://web.archive.org/web/20190121220947/http: //devoluk.com/google-chrome-math-random-issue.html

似乎冲突仅发生在Math.random的前几个调用中。因为如果您只运行上面的createGUID / testGUIDs方法(这显然是我尝试的第一件事),则它不会发生任何冲突。

因此,要进行全面测试,需要重启Google Chrome,生成32字节,重启Chrome,生成,重启,生成...


2
非常令人担忧-有人提出了错误报告吗?
UpTheCreek 2011年

1
尤其像javascript中指向更好随机数生成器的链接一样:baagoe.com/en/RandomMusings/javascript
Leopd 2011年

不幸的是,所说的链接现在已经断开了:(
Gus


7
任何人都可以确认是否已解决此错误吗?
Xdrone

20

只是为了让其他人都知道这一点-使用此处提到的UUID生成技术,我遇到了数量惊人的明显碰撞。即使在我为随机数生成器切换到种子随机性之后,这些冲突仍在继续。可以想象,那让我把头发扯了。

最终,我发现问题(几乎是?)完全与Google的网络爬虫机器人有关。当我开始在用户代理字段中忽略使用“ googlebot”的请求时,冲突就消失了。我猜想他们必须以某种半智能的方式缓存JS脚本的结果,最终结果是它们的蜘蛛式浏览器无法像普通浏览器那样运行。

仅供参考。


2
我们的指标系统遇到了同样的问题。使用“ node-uuid”模块在浏览器中生成会话ID时,看到数千次UUID冲突。原来一直是googlebot。谢谢!
domkck

4

我想将此作为对您问题的评论,但显然StackOverflow不允许我这样做。

我刚刚使用您发布的UUID算法在Chrome中进行了100,000次迭代的基本测试,没有碰撞。这是一个代码片段:

var createGUID = function() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
}

var testGUIDs = function(upperlimit) {
    alert('Doing collision test on ' + upperlimit + ' GUID creations.');
    var i=0, guids=[];
    while (i++<upperlimit) {
        var guid=createGUID();
        if (guids.indexOf(guid)!=-1) {
            alert('Collision with ' + guid + ' after ' + i + ' iterations');
        }
        guids.push(guid);
    }
    alert(guids.length + ' iterations completed.');
}

testGUIDs(100000);

您确定这里没有其他事情吗?


4
是的,我也进行了一些本地测试,没有碰撞。在不同用户的计算机上生成的UUID之间会发生冲突。我可能需要在不同的计算机上生成一些数据并检查冲突。
Muxa 2011年

2
另外,我注意到碰撞发生在间隔3-4周的UUID之间。
Muxa 2011年

很奇怪。您在什么平台上运行?
user533676 2011年

1
V8的Math.random()中似乎没有这么基本的缺陷,但是如果您想尝试使用Chromium 11,则可以使用window.crypto.getRandomValues API添加对强随机数生成的支持。参见blog.chromium.org/2011/06/…
user533676 2011年

在Windows 7和Windows XP的组合上运行。
Muxa

3

最初发布此UUID解决方案的答案已于2017-06-28更新:

一个来自Chrome的开发者很好的文章讨论的在Chrome,Firefox和Safari的Math.random PRNG质量状态。tl; dr-截至2015年末,它的“相当好”,但没有加密质量。为了解决该问题,以下是上述解决方案的更新版本,其中使用了ES6,cryptoAPI和一些JS向导,我对此不以为然

function uuidv4() {
  return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
    (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
  )
}

console.log(uuidv4());


0

此处的答案涉及“是什么原因引起的?” (Chrome Math.random种子问题),但不是“如何避免呢?”。

如果您仍在寻找避免该问题的方法,那我早就写了这个答案,作为对Broofa函数的修改,以解决这个确切的问题。它通过以时间戳的十六进制部分偏移前13个十六进制数字的方式工作,这意味着即使Math.random在同一种子上,除非在完全相同的毫秒内生成,否则它仍将生成不同的UUID。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.