web-dev-qa-db-ja.com

Guid.NewGuid()VS Random.Next()からのランダム文字列ジェネレーター

同僚と私は、データベースでの識別のためにユーザーIDと投稿IDを自動生成するために、これらの方法のどれを使用するかについて議論しています。

1つのオプションは、Randomの単一インスタンスを使用し、いくつかの有用なパラメーターを使用するため、あらゆる種類の文字列生成ケース(つまり、4桁の数字ピンから20桁の英数字ID)で再利用できます。コードは次のとおりです。

_// This is created once for the lifetime of the server instance
class RandomStringGenerator
{
    public const string ALPHANUMERIC_CAPS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    public const string ALPHA_CAPS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    public const string NUMERIC = "1234567890";

    Random Rand = new Random();
    public string GetRandomString(int length, params char[] chars)
    {
        string s = "";
        for (int i = 0; i < length; i++)
            s += chars[Rand.Next() % chars.Length];

        return s;
    }
}
_

他のオプションは単に使用することです:

_Guid.NewGuid();
_

MSDNのGuid.NewGuid を参照してください

私たちは両方とも、Guid.NewGuid()が必要に応じて機能することを認識していますが、カスタムメソッドを使用したいです。同じことをしますが、より制御します。

私の同僚は、カスタムメソッドが作成されているため、衝突が発生する可能性が高いと考えています。 Randomの実装については完全には認識していませんが、Guid.NewGuid()と同じくらいランダムであると思います。カスタムメソッドの一般的な使用法は次のとおりです。

_RandomStringGenerator stringGen = new RandomStringGenerator();
string id = stringGen.GetRandomString(20, RandomStringGenerator.ALPHANUMERIC_CAPS.ToCharArray());
_

編集1:

  • キーを生成するための自動インクリメント(または同様の)機能を持たないAzureテーブルを使用しています。
  • ここでのいくつかの答えは、NewGuid()を使用するように私に言っているだけです。私は、Guidと同じ自由度が与えられた場合、cooked upメソッドが衝突を生成する可能性が高い理由について、より深い理由を探しています。

編集2:

また、クックアップメソッドを使用して、セッショントークンとは異なり、WebサイトのURLで表示をきれいにする必要がある投稿IDを生成しました( http://mywebsite.com/14983336 など)。そのため、ここではGUIDはオプションではありませんが、衝突は回避する必要があります。

25
George Powell

Guidと同じ自由度が与えられた場合に、cooked upメソッドが衝突を生成する可能性が高い理由について、より詳細な理由を探しています。

まず、他の人が指摘したように、Randomはスレッドセーフではありません。複数のスレッドから使​​用すると、内部データ構造が破損し、常に同じシーケンスが生成される可能性があります。

次に、Randomが現在の時刻に基づいてシードされます。同じミリ秒内に作成されたRandomの2つのインスタンス(最新のハードウェアではミリ秒が数millionプロセッササイクルであることを思い出してください)は同じシードを持ち、したがって同じシーケンス。

第三に、私は嘘をついた。 Randomは現在の時刻に基づいてシードされません。 マシンがアクティブになっている時間に基づいてシードされます。シードは32ビットの数値であり、粒度はミリ秒単位であるため、ラップされるまで数週間しかかかりません。しかし、それは問題ではありません。問題は:Randomのインスタンスを作成する期間は、マシンの起動から数分以内である可能性が高いです。マシンの電源を入れ直すたびに、またはクラスター内で新しいマシンをオンラインにするたびに、ランダムのインスタンスが作成される小さなウィンドウがあり、それが起こるほど、オッズが大きくなりますあなたが以前持っていた種を入手してください。

(更新:.NET Frameworkの新しいバージョンは、これらの問題のいくつかを緩和しました。これらのバージョンでは、同じミリ秒以内に作成されたすべてのRandomが同じシードを持たなくなります。しかし、Random;暗号強度のランダムではなく、擬似ランダムのみであることを常に忘れないでください。Randomは実際には非常に予測可能であるため、予測不可能性に依存している場合は適切ではありません。)

他の人が言ったように:データベースのプライマリキーが必要な場合は、データベースにプライマリキーを生成させる。データベースに仕事をさせてください。グローバルに一意の識別子が必要な場合は、guidを使用します。それが彼らの目的です。

最後に、GUIDの使用と乱用について詳しく知りたい場合は、「ガイドガイド」シリーズをお読みください。パート1はこちらです:

http://blogs.msdn.com/b/ericlippert/archive/2012/04/24/guid-guide-part-one.aspx

50
Eric Lippert

他の回答に書かれているように、私の実装にはいくつかの深刻な問題がありました。

  • スレッドセーフ:ランダムはスレッドセーフではありません。
  • 予測可能性:このメソッドは、Randomクラスの性質により、セッショントークンなどのセキュリティクリティカルな識別子に使用できませんでした。
  • Collisions:メソッドは20個の「乱数」を作成しましたが、シード値が31ビットであり、不正なソースに由来するため、衝突の確率は_(number of possible chars)^20_ではありません。同じシードを指定すると、anyシーケンスの長さは同じになります。

Guid.NewGuid()は問題ありませんが、URLでinいGUIDを使用したくない場合や。情報はほとんどありません。

現在使用しているコードは次のとおりです。安全で柔軟性があり、私が知る限り、十分な長さと文字を選択すれば衝突を起こす可能性はほとんどありません。

_class RandomStringGenerator
{
    RNGCryptoServiceProvider Rand = new RNGCryptoServiceProvider();
    public string GetRandomString(int length, params char[] chars)
    {
        string s = "";
        for (int i = 0; i < length; i++)
        {
            byte[] intBytes = new byte[4];
            Rand.GetBytes(intBytes);
            uint randomInt = BitConverter.ToUInt32(intBytes, 0);
            s += chars[randomInt % chars.Length];
        }
        return s;
    }
}
_
7
George Powell

「データベースでの識別のためのユーザーIDと投稿IDの自動生成」...データベースシーケンスまたはIDを使用してキーを生成しないのはなぜですか?

私にとって、あなたの質問は本当に「データベースに主キーを生成する最良の方法は何ですか?」です。その場合、データベースの従来のツールを使用する必要があります。これは、シーケンスまたはIDのいずれかになります。これらには、生成された文字列よりも利点があります。

  1. シーケンス/アイデンティティインデックスの改善。 GUIDなどが貧弱なインデックスを作成する理由を説明する多数の記事とブログ投稿があります。
  2. テーブル内で一意であることが保証されています
  3. 衝突することなく、同時挿入により安全に生成できます
  4. 実装が簡単です

次の質問は、GUIDや生成された文字列をどのような理由で検討しているのでしょうか?分散データベース間で統合しますか?そうでない場合は、存在しない問題を解決しているかどうかを自問する必要があります。

4
Jordan Parmer

カスタムメソッドには2つの問題があります。

  1. Randomのグローバルインスタンスを使用しますが、ロックは使用しません。 =>マルチスレッドアクセスは、その状態を破壊する可能性があります。その後、出力はそれが既に行うよりもさらに多くを吸い込みます。
  2. 予測可能な31ビットシードを使用します。これには2つの結果があります。
    • 推測不可能性が重要なセキュリティ関連の用途には使用できません
    • 小さなシード(31ビット)は、数値の品質を低下させる可能性があります。たとえば、Randomの複数のインスタンスを同時に作成した場合(システムの起動以降)、おそらく同じ乱数シーケンスが作成されます。

これは、Randomの出力がどれほど長くても一意であることを信頼できないことを意味します。

セキュリティが必要ない場合でも、CSPRNG( RNGCryptoServiceProvider )を使用することをお勧めします。そのパフォーマンスは、ほとんどの用途で依然として許容可能であり、Random上の乱数の品質を信頼します。一意性が必要な場合は、約128ビットの数値を取得することをお勧めします。

RNGCryptoServiceProviderを使用してランダムな文字列を生成するには、 C#で8文字の英数字の文字列をランダムに生成するにはどうすればよいですか に対する回答をご覧ください。


現在、Guid.NewGuid()によって返されるGUIDはバージョン4のGUIDです。これらはPRNGから生成されるため、ランダムな122ビット数を生成するのとかなり似た特性を持っています(残りの6ビットは固定されています)。そのエントロピーソースは、Randomが使用するものよりもはるかに高い品質を備えていますが、暗号的に安全であるとは限りません。

ただし、生成アルゴリズムはいつでも変更できるため、それに依存することはできません。たとえば、過去にWindows GUID=生成アルゴリズムはv1(MAC +タイムスタンプに基づく)からv4(ランダム)に変更されました。

3
CodesInChaos

使用する - System.Guid そのまま:

...一意の識別子が必要なすべてのコンピューターとネットワークで使用できます。

Random擬似乱数ジェネレーターであることに注意してください。それは本当にランダムでもユニークでもありません。 128ビットのGUIDと比較して、32ビットの値しか使用できません。

ただし、GUIDでさえ衝突する可能性があります(可能性は非常にわずかですが)ので、データベースの独自の機能を使用して一意の識別子(自動インクリメントID列など)を与える必要があります。また、GUID=を4または20(英数字)の数字に簡単に変換することはできません。

一部の人々がコメントで述べたことに反して、GUID Guid.NewGuid()によって生成されたGUIDは、マシン固有の識別子に依存していません(タイプ1 GUIDのみ、Guid.NewGuid()ほとんどランダムなタイプ4のGUIDを返します)。

暗号化セキュリティを必要としない限り、Randomクラスで十分ですが、さらに安全にしたい場合はSystem.Security.Cryptography.RandomNumberGeneratorを使用してください。 Guidアプローチでは、GUIDのすべての数字がランダムではないことに注意してください。 wikipedia からの引用:

正規表現xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxxでは、Nの最上位ビットはバリアントを示します(バリアントに応じて、1、2、または3ビットが使用されます)。 UUID仕様の対象となるバリアントは、Nの最上位2ビットが1 0であることで示されます(つまり、16進数のNは常に8、9、A、またはBになります)。 UUID仕様の対象となるバリアントには、5つのバージョンがあります。このバリアントでは、Mの4ビットはUUIDバージョンを示します(つまり、16進数のMは1、2、3、4、または5になります)。

1
erikkallen

編集に関して、生成された文字列よりもGUID=を好む理由の1つを次に示します。

SQL ServerのGUID(一意の識別子))のネイティブストレージは16バイトです。idの各「数字」が文字として保存される同等の長さのvarchar(文字列)を保存するには、フォーマットに応じて、32〜38バイトが必要になります。

SQL Serverは、その記憶域により、varchar列よりもuniqueidentifier列に効率的にインデックスを付けることもできます。

0
GalacticCowboy