program story

JavaScript에서 UUID를 생성 할 때 충돌이 발생합니까?

inputbox 2020. 9. 6. 09:54
반응형

JavaScript에서 UUID를 생성 할 때 충돌이 발생합니까?


이것은 이 질문 과 관련 있습니다. 이 답변의 아래 코드를 사용하여 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 크롬에서 실행되는 웹 앱.
  • 16 명의 사용자.
  • 이러한 사용자에 의해 지난 2 개월 동안 약 4000 개의 UUID가 생성되었습니다.
  • 약 20 건의 충돌이 발생했습니다. 예를 들어 오늘 생성 된 새 UUID가 약 2 개월 전과 동일했습니다 (다른 사용자).

이 문제의 원인은 무엇이며 어떻게 방지 할 수 있습니까?


내 추측으로는 Math.random()어떤 이유로 시스템에서 고장났다 는 것 입니다 (그렇게 들리 겠지만 기이함). 이것은 내가 본 사람이 충돌을 겪는 첫 번째 보고서입니다.

node-uuid해당 코드에서 16 진수 분포를 테스트하는 데 사용할 수 있는 테스트 도구 가 있습니다. 괜찮아 보이면 그렇지 않은 Math.random()것이므로 사용중인 UUID 구현을 uuid()메서드 로 대체하고 여전히 좋은 결과를 얻는 지 확인하십시오.

[업데이트 : 방금 시작할 때 버그에 대한 Veselin의 보고서를 보았습니다 Math.random(). 문제는 시작시에만 발생하므로 node-uuid테스트가 유용하지 않을 수 있습니다. devoluk.com 링크에서 더 자세히 설명하겠습니다.]


실제로 충돌이 있지만 Google 크롬에서만 발생합니다. 여기에서 주제에 대한 내 경험을 확인하십시오.

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 메서드를 실행하면 (분명히 내가 시도한 첫 번째) 충돌이 전혀 발생하지 않습니다.

따라서 전체 테스트를 수행하려면 Chrome을 다시 시작하고, 32 바이트를 생성하고, Chrome을 다시 시작하고, 생성하고, 다시 시작하고, 생성해야합니다.


다른 사람들이이 사실을 알 수 있도록 여기에 언급 된 UUID 생성 기법을 사용하여 놀랍도록 많은 수의 명백한 충돌이 발생했습니다. 이러한 충돌은 난수 생성기 에서 seedrandom 으로 전환 한 후에도 계속되었습니다 . 당신이 상상할 수 있듯이 그로 인해 머리카락이 찢어졌습니다.

나는 결국 문제가 Google의 웹 크롤러 봇과 관련된 (거의?) 독점적이라는 것을 알게되었습니다. user-agent 필드에서 "googlebot"을 사용하여 요청을 무시하기 시작하자 충돌이 사라졌습니다. 나는 그들이 JS 스크립트의 결과를 반 지능적인 방식으로 캐시해야한다고 생각하는데, 최종 결과는 그들의 스파이더 링 브라우저가 일반 브라우저가하는 방식으로 작동한다고 믿을 수 없다는 것입니다.

그냥 참고로.


나는 이것을 귀하의 질문에 대한 의견으로 게시하고 싶었지만 분명히 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);

여기서 다른 일이없는 게 확실합니까?


이 UUID 솔루션을 처음 게시 한 답변 은 2017-06-28에 업데이트되었습니다.

Chrome, Firefox 및 Safari의 Math.random PRNG 품질 상태에 대해 설명하는 Chrome 개발자좋은 기사입니다 . 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());


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.

참고URL : https://stackoverflow.com/questions/6906916/collisions-when-generating-uuids-in-javascript

반응형