问题
This relates to this question. I am using the code below from this answer to generate UUID in JavaScript:
\'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).
What is causing this issue and how can I avoid it?
回答1:
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.
node-uuid
has a test harness that 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.
[Update: Just saw Veselin's report about the bug with Math.random()
at startup. Since the problem is only at startup, the node-uuid
test is unlikely to be useful. I'll comment in more detail on the devoluk.com link.]
回答2:
Indeed there are collisions but only under Google Chrome. Check out my experience on the topic here
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.)
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.
So to make a full test one needs to restart Google Chrome, generate 32 byte, restart Chrome, generate, restart, generate...
回答3:
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 seedrandom for my random number generator. That had me tearing my hair out, as you can imagine.
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.
Just an FYI.
回答4:
I wanted to post this as a comment to your question, but apparently StackOverflow won't let me.
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:
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?
回答5:
The answer that originally posted this UUID solution was updated on 2017-06-28:
A good article from Chrome developers discussing 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
crypto
API, and a bit of JS wizardy I can't take credit for:
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());
回答6:
The answers here deal with "what's causing the issue?" (Chrome Math.random seed issue) but not "how can I avoid it?".
If you are still looking for how to avoid this issue, I wrote this answer a 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.
来源:https://stackoverflow.com/questions/6906916/collisions-when-generating-uuids-in-javascript