web-dev-qa-db-ja.com

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 Chromeで実行されているWebアプリ。
  • 16ユーザー。
  • これらのユーザーによって、過去2か月間に約4000のUUIDが生成されました。
  • 約20回衝突しました-例今日生成された新しいUUIDは、約2か月前と同じでした(異なるユーザー)。

questionsは次のとおりです。

  1. 問題の原因は何ですか?
  2. どうすれば回避できますか?
90
Muxa

私の最善の推測は、Math.random()が何らかの理由でシステム上で壊れていることです(奇妙に聞こえますが)。これは、誰もが衝突を経験した最初のレポートです。

_node-uuid_には テストハーネス があり、このコードで16進数の分布をテストするために使用できます。それが問題ないようであれば、Math.random()ではないので、使用しているUUID実装をuuid()メソッドに代入してみて、まだ良い結果が得られるかどうかを確認してください。

[更新:起動時のMath.random()のバグに関するVeselinのレポートを見たところです。問題は起動時のみであるため、_node-uuid_テストは有用ではありません。 devoluk.comリンクについて詳しくコメントします。]

33
broofa

実際、衝突はありますが、Google Chromeでのみ発生します。トピックに関する私の経験をここでチェックしてください

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

衝突はMath.randomの最初の数回の呼​​び出しでのみ発生するようです。原因は、上記のcreateGUID/testGUIDsメソッドを実行しただけの場合(明らかに最初に試したことです)、まったく衝突せずに動作します。

したがって、完全なテストを行うには、Google Chromeを再起動し、32バイトを生成し、Chromeを再起動し、生成し、再起動し、生成する必要があります...

35
Veselin Kulov

他の人がこれに気付くことができるように-ここで述べたUUID生成技術を使用して、驚くほど多くの明らかな衝突に遭遇しました。これらの衝突は、乱数ジェネレーターで seedrandom に切り替えた後も続きました。あなたが想像できるように、それは私が私の髪を引き裂きました。

最終的に、問題は(ほとんど?)GoogleのWebクローラーボットにのみ関連していることがわかりました。ユーザーエージェントフィールドで「googlebot」のリクエストを無視し始めるとすぐに、衝突はなくなりました。 JSスクリプトの結果を半インテリジェントな方法でキャッシュする必要があり、その結果、スパイダーブラウザーが通常のブラウザーのように動作することを期待できないと推測しています。

ちょっとだけ。

18
Ken Smith

私はこれをあなたの質問へのコメントとして投稿したかったのですが、どうやら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
user533676

このUUIDソリューションを最初に投稿した回答 は2017-06-28に更新されました:

Chrome開発者からの 良い記事 Chrome、Firefox、およびSafariでのMath.random PRNG品質の状態について説明しています。 tl; dr-2015年後半の時点では「かなり良い」が、暗号品質ではない。その問題に対処するために、ES6、crypto AP​​I、および 私が信用できない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());
3
Luke M Willis

ここでの回答は、「問題の原因は何ですか?」に関するものです。 (Chrome Math.random seedの問題)ではなく、「どうすれば回避できますか?」.

この問題を回避する方法をまだ探している場合は、この正確な問題を回避するためにBroofaの関数を修正したものとして this answer を書きました。タイムスタンプの16進数部分で最初の13桁の16進数をオフセットすることにより機能します。つまり、Math.randomが同じシード上にある場合でも、まったく同じミリ秒で生成されない限り、異なるUUIDを生成します。

0
Briguy37