27

私の同僚と私は、データベースでの識別のためにユーザー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と同じ自由度が与えられた場合に、クックアップされたメソッドが衝突を生成する可能性が高い理由について、より詳細な理由を探しています。

編集2:

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

4

7 に答える 7

53

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

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

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

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

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

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

そして最後に、GUIDの使用法と乱用について詳しく知りたい場合は、私の「GUIDガイド」シリーズを読むことをお勧めします。パート1はここにあります:

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

于 2013-02-20T16:27:19.913 に答える
7

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

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

Guid.NewGuid()URLと.NETで醜いGUIDを使用したくない場合を除いて、問題ありません。NewGuid()アルゴリズムは、セッショントークンで使用するために暗号的に安全であることが知られていないため、少しの情報がわかっていれば、予測可能な結果が得られる可能性があります。

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

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;
    }
}
于 2013-02-21T11:50:28.087 に答える
4

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

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

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

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

于 2013-02-20T15:22:02.827 に答える
1

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

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

正規表現では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 のいずれかになります)。

于 2013-02-20T15:34:35.303 に答える
1

そのまま使用System.Guid

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

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

ただし、GUID でさえ競合する可能性があるため (可能性は非常に低いですが)、データベース独自の機能を使用して一意の識別子 (自動インクリメント ID 列など) を指定する必要があります。また、GUID を 4 桁または 20 桁の数字に簡単に変更することはできません。

于 2013-02-20T15:21:58.473 に答える
0

編集に関して、生成された文字列よりもGUIDを優先する理由の1つは次のとおりです。

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

SQL Serverはストレージがあるため、varchar列よりも効率的にuniqueidentifier列にインデックスを付けることもできます。

于 2013-02-20T15:58:49.807 に答える