69

データベース内でプライマリキーとして使用されたときに、誰かがシーケンシャルGUIDと標準GUIDのパフォーマンスを測定したことがありますか?


一意のキーが推測可能かどうかはわかりません。WebUIまたは他の部分からそれらを渡すこと自体は悪い習慣のようです。セキュリティ上の懸念がある場合は、GUIDの使用がどのように改善されるかわかりません。物事(これが問題である場合は、フレームワークの適切な暗号化機能を使用して実際の乱数ジェネレーターを使用してください)。
他の項目は私のアプローチでカバーされています。シーケンシャルGUIDは、DBアクセスを必要とせずにコードから生成でき(Windowsの場合のみ)、時間と空間がユニークです。
そして、はい、質問はそれに答えることを目的として提起されました。PKにGuidを選択した人々に、データベースの使用を改善する方法を提供するためです(私の場合、顧客はサーバーを変更することなく、はるかに高いワークロードを維持できます)。

セキュリティ上の懸念は非常に大きいようです。この場合、Sequential Guidを使用しないか、UIから前後に渡されるPK用の標準Guidと、その他すべてのシーケンシャルGUIDを使用してください。いつも絶対的な真実はないので、これを反映するためにメインアンサーも編集しました。

4

8 に答える 8

114

GUIDとシーケンシャルGUID



典型的なパターンは、テーブルのPKとしてGuidを使用することですが、他の説明で言及されているように(GUID / UUIDデータベースキーの長所と短所を参照)、いくつかのパフォーマンスの問題があります。

これは典型的なGUIDシーケンスです

f3818d69-2552-40b7-a403-01a6db4552f7
7ce31615-fafb-42c4-b317-40d21a6a3c6094732fc7-768e-4cf2-9107-f0953f6795a5
この


種のデータの問題は次のとおりです。<
-

  • 値の幅広い分布
  • ほぼランダムなもの
  • インデックスの使用は非常に、非常に、非常に悪い
  • たくさんの葉が動く
  • ほとんどすべてのPKは、少なくとも非クラスター化インデックス上にある必要があります
  • OracleとSQLServerの両方で問題が発生する



考えられる解決策は、次のように生成されるシーケンシャルGUIDを使用することです

。cc6466f7-1066-11dd-
acb6-005056c00008cc6466f8-1066-11dd-acb6-005056c00008
cc6466f9-1066-11dd-acb6-005056c00008


C#コードからそれらを生成する方法:

[DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(out Guid guid);

public static Guid SequentialGuid()
{
    const int RPC_S_OK = 0;
    Guid g;
    if (UuidCreateSequential(out g) != RPC_S_OK)
        return Guid.NewGuid();
    else
        return g;
}


利点

  • インデックスのより良い使用法
  • クラスタ化されたキーの使用を許可する(NLBシナリオで検証される)
  • ディスク使用量の削減
  • 最小コストでパフォーマンスの20〜25%向上



実際の測定: シナリオ:

  • SQLServerにUniqueIdentifierタイプとして保存されたGUID
  • OracleにCHAR(36)として格納されているGUID
  • 単一のトランザクションで一緒にバッチ処理された多くの挿入操作
  • テーブルに応じて1から数百のインサート
  • 1000万行を超える一部のテーブル



ラボテスト– SQL Server VS2008テスト、10人の同時ユーザー、思考時間なし、リーフテーブルの標準GUID平均

で600回の挿入を含むベンチマークプロセス 。処理時間:10.5秒 平均 秒の要求:54.6平均 それぞれ 時間:0.26シーケンシャルGUID平均 処理時間:平均4.6秒 秒の要求:87.1平均 それぞれ 時間:0.12 Oracleでの結果(申し訳ありませんが、テストに使用した別のツール)1.327.613 Guid PK標準GUID0.02秒のテーブルに挿入します。各インサートの経過時間、2.861秒。CPU時間の合計












31.049秒 経過し た

シーケンシャルGUID0.00秒。各インサートの経過時間、1.142秒。CPU時間の合計3.667秒。経過

DBファイルの順次読み取り待機時間は、62.415秒の640万の待機イベントから11.063秒の120万の待機イベントに渡されました。

すべてのシーケンシャルGUIDを推測できることを確認することが重要です。したがって、セキュリティが懸念される場合は、標準のGUIDを使用したまま、それらを使用することはお勧めできません。
簡単に言うと...GuidをPKとして使用する場合、UIから前後に渡されないたびに順次GUIDを使用すると、操作が高速化され、実装に費用はかかりません。

于 2008-10-04T13:52:51.390 に答える
62

ここで何かが欠けている可能性があります (間違っている場合はお気軽に修正してください) が、主キーに連続した GUID/UUID を使用するメリットはほとんどありません。

自動インクリメント整数よりも GUID または UUID を使用するポイントは次のとおりです

  • データベースに接続せずにどこでも作成できます
  • これらは、アプリケーション内で完全に一意の識別子です (UUID の場合は、普遍的に一意です)。
  • 1 つの識別子が与えられた場合、巨大なキースペースを総当たり攻撃する以外に、次または前の (または他の有効な識別子でさえ) を推測する方法はありませ

残念ながら、あなたの提案を使用すると、それらすべてが失われます。

あ、はい。GUID が改善されました。しかし、その過程で、そもそもそれらを使用する理由のほとんどすべてを捨ててしまいました.

本当にパフォーマンスを向上させたい場合は、標準の自動インクリメント整数主キーを使用してください。これにより、説明したすべての利点 (およびそれ以上) が提供され、ほとんどすべての点で「順次 GUID」よりも優れています。

これはあなたの質問に明確に答えていないため、忘却に陥る可能性が最も高いですが(これは明らかに慎重に作成されているため、すぐに自分で答えることができます)、提起することははるかに重要なポイントだと思います.

于 2008-10-04T15:36:59.230 に答える
23

Massimogentilini が既に述べたように、UuidCreateSequential を使用すると (コードで GUID を生成するとき)、パフォーマンスを向上させることができます。しかし、事実が欠けているようです: SQL Server (少なくとも Microsoft SQL 2005 / 2008) は同じ機能を使用しますが、.NET と SQL Server では Guid の比較/順序付けが異なり、IO がさらに発生します。 GUID が正しく順序付けられないためです。SQL サーバー用に正しく順序付けされた GUID を生成するには (順序付け)、次のことを行う必要があります (比較の詳細を参照)。

[System.Runtime.InteropServices.DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(byte[] buffer);

static Guid NewSequentialGuid() {

    byte[] raw = new byte[16];
    if (UuidCreateSequential(raw) != 0)
        throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());

    byte[] fix = new byte[16];

    // reverse 0..3
    fix[0x0] = raw[0x3];
    fix[0x1] = raw[0x2];
    fix[0x2] = raw[0x1];
    fix[0x3] = raw[0x0];

    // reverse 4 & 5
    fix[0x4] = raw[0x5];
    fix[0x5] = raw[0x4];

    // reverse 6 & 7
    fix[0x6] = raw[0x7];
    fix[0x7] = raw[0x6];

    // all other are unchanged
    fix[0x8] = raw[0x8];
    fix[0x9] = raw[0x9];
    fix[0xA] = raw[0xA];
    fix[0xB] = raw[0xB];
    fix[0xC] = raw[0xC];
    fix[0xD] = raw[0xD];
    fix[0xE] = raw[0xE];
    fix[0xF] = raw[0xF];

    return new Guid(fix);
}

またはこのリンクまたはこのリンク

于 2009-06-25T08:06:54.103 に答える
5

この記事を参照してください:(http://www.shirmanov.com/2010/05/generated-newsequentialid-compatible.html

MSSqlはこれと同じ関数を使用してNewSequencialIds(UuidCreateSequential(out Guid guid))を生成しますが、MSSQLは3番目と4番目のバイトパターンを逆にします。これにより、コードでこの関数を使用した場合と同じ結果が得られません。Shirmanovは、MSSQLが作成するのとまったく同じ結果を取得する方法を示しています。

于 2011-02-18T05:01:44.940 に答える
4

Entity Framework を使用して、Guid (クラスター化されたものとクラスター化されていないもの)、Sequential Guid および int (Identity/autoincrement) の違いを混乱させました。Sequential Guid は、ID を持つ int に比べて驚くほど高速でした。Sequential Guid の結果とコードはこちら.

于 2013-11-01T20:42:52.320 に答える
4

Jimmy Nilsson による COMBを確認してください。これは、多くのビットがタイムスタンプのような値に置き換えられた GUID の一種です。これは、COMB を順序付けできることを意味し、主キーとして使用すると、新しい値を挿入するときのインデックス ページの分割が少なくなります。

UniqueIdentifier (GUID) を主キーとして使用しても問題ありませんか?

于 2008-10-04T15:46:26.107 に答える
4

シーケンシャル GUId を使用する必要がある場合、SQL Server 2005 は関数を使用してそれらを生成できますNEWSEQUENTIALID()

ただし、GUId の基本的な使用法は、推測できないキー (または代替キー) を生成することであるため (たとえば、推測されたキーを GET で渡さないようにするため)、推測されやすいため、それらがどの程度適用できるかわかりません。

MSDNから:

重要:
プライバシーが懸念される場合は、この機能を使用しないでください。次に生成される GUID の値を推測できるため、その GUID に関連付けられたデータにアクセスできます。

于 2008-10-04T15:59:44.943 に答える
4

わかりました、私はついに自分でデザインと生産のこのポイントに到達しました.

上位 32 ビットがミリ秒単位の Unix 時間のビット 33 から 1 に基づいている COMB_GUID を生成します。したがって、2 ミリ秒ごとに 93 ビットのランダム性があり、上位ビットのロールオーバーは 106 年ごとに発生します。COMB_GUID (またはタイプ 4 UUID) の実際の物理表現は、22 文字の文字列である 128 ビットの base64 エンコード バージョンです。

postgres に挿入する場合、完全にランダムな UUID と COMB _GUID の間の速度の比率は、COMB_GUID にとって有益なものとして保持されます。COMB_GUID は、100 万レコードのテストで、複数のテストで私のハードウェアで2 倍速くなりました。レコードには、id (22 文字)、文字列フィールド (110 文字)、倍精度、および INT が含まれます。

ElasticSearch では、インデックス作成に関して 2 つの間に識別可能な違いはありません。コンテンツが時間に関連してフィードされるため、コンテンツがチェーン内のどこかの BTREE インデックスに移動する場合、または id フィールドで事前に並べ替えて、時間に関連し、部分的にシーケンシャルになるようにする場合に備えて、COMB_GUIDS を引き続き使用します速度が向上します。

とても興味深い。COMB_GUID を作成する Java コードは次のとおりです。

import java.util.Arrays;
import java.util.UUID;
import java.util.Base64; //Only avail in Java 8+
import java.util.Date;

import java.nio.ByteBuffer; 

    private ByteBuffer babuffer = ByteBuffer.allocate( (Long.SIZE/8)*2 );
private Base64.Encoder encoder = Base64.getUrlEncoder();
public  String createId() {
    UUID uuid = java.util.UUID.randomUUID();
        return uuid2base64( uuid );
}

    public String uuid2base64(UUID uuid){ 

        Date date= new Date();
        int intFor32bits;
        synchronized(this){
        babuffer.putLong(0,uuid.getLeastSignificantBits() );
        babuffer.putLong(8,uuid.getMostSignificantBits() );

                long time=date.getTime();
        time=time >> 1; // makes it every 2 milliseconds
                intFor32bits = (int) time; // rolls over every 106 yers + 1 month from epoch
                babuffer.putInt( 0, intFor32bits);

    }
        //does this cause a memory leak?
        return encoder.encodeToString( babuffer.array() );
    }

}

于 2013-09-26T21:54:29.620 に答える