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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6906916/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 23:42:42  来源:igfitidea点击:

Collisions when generating UUIDs in JavaScript?

javascriptrandomuuidcollision

提问by Muxa

This relates to this question. I am using the code below from this answerto generate UUID in JavaScript:

这与这个问题有关。我正在使用此答案中的以下代码在 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);
});

This solution appeared to be working fine, but I am getting collisions. Here's what I have:

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

  • A web-app running in Google Chrome.
  • 16 users.
  • about 4000 UUIDs have been generated in the past 2 months by these users.
  • I got about 20 collisions - e.g. new UUID generated today was the same as about 2 months ago (different user).
  • 在 Google Chrome 中运行的网络应用程序。
  • 16 个用户。
  • 这些用户在过去 2 个月内生成了大约 4000 个 UUID。
  • 我遇到了大约 20 次冲突 - 例如,今天生成的新 UUID 与大约 2 个月前相同(不同的用户)。

What is causing this issue and how can I avoid it?

是什么导致了这个问题,我该如何避免?

采纳答案by broofa

My best guess is that Math.random()is broken on your system for some reason (bizarre as that sounds). This is the first report I've seen of anyone getting collisions.

我最好的猜测是Math.random()由于某种原因,它在您的系统上损坏了(听起来很奇怪)。这是我第一次看到有人发生碰撞的报告。

node-uuidhas a test harnessthat you can use to test the distribution of hex digits in that code. If that looks okay then it's not Math.random(), so then try substituting the UUID implementation you're using into the uuid()method there and see if you still get good results.

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

[Update: Just saw Veselin's reportabout the bug with Math.random()at startup. Since the problem is only at startup, the node-uuidtest is unlikely to be useful. I'll comment in more detail on the devoluk.com link.]

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

回答by Veselin Kulov

Indeed there are collisions but only under Google Chrome. Check out my experience on the topic here

确实存在冲突,但仅在 Google Chrome 下。在此处查看我在该主题上的经验

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

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

(Link broken as of 2019. Archive link: https://web.archive.org/web/20190121220947/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。)

Seems like collisions only happen on the first few calls of Math.random. Cause if you just run the createGUID / testGUIDs method above (which obviously was the first thing I tried) it just works with no collisions whatsoever.

似乎碰撞只发生在 Math.random 的前几次调用中。因为如果你只是运行上面的 createGUID / testGUIDs 方法(这显然是我尝试的第一件事),它只会在没有任何冲突的情况下工作。

So to make a full test one needs to restart Google Chrome, generate 32 byte, restart Chrome, generate, restart, generate...

所以要进行完整的测试,需要重启谷歌浏览器,生成 32 字节,重启 Chrome,生成,重启,生成...

回答by Ken Smith

Just so that other folks can be aware of this - I was running into a surprisingly large number of apparent collisions using the UUID generation technique mentioned here. These collisions continued even after I switched to seedrandomfor my random number generator. That had me tearing my hair out, as you can imagine.

只是为了让其他人能够意识到这一点 - 我使用这里提到的 UUID 生成技术遇到了数量惊人的明显冲突。即使在我为随机数生成器切换到seedrandom之后,这些冲突仍在继续。你可以想象,这让我把头发扯掉了。

I eventually figured out that the problem was (almost?) exclusively associated with Google's web crawler bots. As soon as I started ignoring requests with "googlebot" in the user-agent field, the collisions disappeared. I'm guessing that they must cache the results of JS scripts in some semi-intelligent way, with the end result that their spidering browser can't be counted on to behave the way that normal browsers do.

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

Just an FYI.

仅供参考。

回答by user533676

I wanted to post this as a comment to your question, but apparently StackOverflow won't let me.

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

I just ran a rudimentary test of 100,000 iterations in Chrome using the UUID algorithm you posted and got no collisions. Here's a code snippet:

我刚刚使用您发布的 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);

Are you sure there isn't something else going on here?

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

回答by Luke

The answerthat originally posted this UUID solution was updated on 2017-06-28:

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

A good article from Chrome developersdiscussing the state of Math.random PRNG quality in Chrome, Firefox, and Safari. tl;dr - As of late-2015 it's "pretty good", but not cryptographic quality. To address that issue, here's an updated version of the above solution that uses ES6, the cryptoAPI, and a bit of JS wizardy I can't take credit for:

一个来自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());

回答by Briguy37

The answers here deal with "what's causing the issue?" (Chrome Math.random seed issue) but not "how can I avoid it?".

这里的答案涉及“是什么导致了问题?” (Chrome Math.random 种子问题)但不是“我如何避免它?”。

If you are still looking for how to avoid this issue, I wrote this answera while back as a modified take on Broofa's function to get around this exact problem. It works by offsetting the first 13 hex numbers by a hex portion of the timestamp, meaning that even if Math.random is on the same seed it will still generate a different UUID unless generated at the exact same millisecond.

如果您仍在寻找如何避免这个问题,我不久前写了这个答案,作为对 Broofa 函数的修改,以解决这个确切的问题。它的工作原理是用时间戳的十六进制部分偏移前 13 个十六进制数字,这意味着即使 Math.random 在同一个种子上,它仍然会生成不同的 UUID,除非在完全相同的毫秒内生成。