web-dev-qa-db-ja.com

Sequential Guidの標準のGuidに対するパフォーマンスの向上は何ですか?

データベース内で主キーとして使用した場合、シーケンシャルGUIDと標準GUIDのパフォーマンスを測定したことがありますか?

64

GUIDとシーケンシャルGUID



テーブルのPKとしてGuidを使用する典型的なパターンですが、他の議論で参照されています( GUID/UUIDデータベースキー)の利点と欠点を参照 =)いくつかのパフォーマンスの問題があります。

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

f3818d69-2552-40b7-a403-01a6db4552f7
7ce31615-fafb-42c4-b317-40d21a6a3c60
94732fc7-768e-4cf2-9107-f0953f6795a5


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

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



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

cc6466f7-1066-11dd-acb6-005056c00008
cc6466f8-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%のパフォーマンス向上



実際の測定:シナリオ:

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



ラボテスト– SQL Server

VS2008テスト、同時ユーザー10人、思考時間なし、リーフテーブルのバッチで600挿入を行うベンチマークプロセス
標準ガイド
平均処理時間:10.5
平均秒の要求:54.6
平均応答時間:0.26

順次ガイド
平均プロセス時間:4.6
平均秒の要求:87.1
平均応答時間:0.12

Oracleでの結果(申し訳ありませんが、テストに別のツールが使用されました)1.327.613 GUIDのPKを含むテーブルに挿入

標準ガイド0.02秒。各挿入の経過時間、2.861秒。 CPU時間の合計、31.049秒の合計。経過

順次ガイド0.00秒。各挿入の経過時間、1.142秒。 CPU時間の合計3.667秒。経過

6.4数百万の待機イベントから渡されたDBファイルの順次読み取り待機時間62.415秒から1.2100万の待機イベント11.063秒。

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

104

私はここで何か不足しているかもしれません(私がいる場合は自由に修正してください)が、主キーに順次GUID/UUIDを使用してもほとんどメリットがありません。

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

  • それらはどこにでも作成できますなしデータベースに接続します
  • これらは、アプリケーション内で完全に一意の識別子です(UUIDの場合は、普遍的に一意です)。
  • 1つの識別子が与えられた場合、ブルートフォーシングhugeキースペースの外では、次または前(またはany他の有効な識別子)を推測する方法はありません。

残念ながら、あなたの提案を使用して、あなたはallそれらのものを失います。

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

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

これはあなたの質問に明確に答えられないので(おそらくあなたがすぐにあなた自身で答えることができるように注意深く作成されている)、忘却の中にダウンモッド化される可能性が高いですが、私はそれが提起するはるかに重要なポイントだと感じています。

52
Dan

Massimogentiliniがすでに述べたように、UuidCreateSequentialを使用すると(コードでGUIDを生成するときに)パフォーマンスを改善できます。しかし、事実が欠けているようです:SQL Server(少なくともMicrosoft SQL 2005/2008)は同じ機能を使用しますが、Guidの比較/順序付けは.NETとSQL Serverで異なります。 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);
}

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

20

順次GUIを使用する必要がある必要がある場合、SQL Server 2005はNEWSEQUENTIALID()関数を使用してそれらを生成できます。

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

から [〜#〜] msdn [〜#〜]

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

4
Sklivvz

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

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

4
Bryon

チェックアウト COMBs by Jimmy Nilsson:タイプGUIDここで、ビット数はタイムスタンプのような値に置き換えられています。これは、COMBを注文できることを意味しますを主キーとして使用すると、新しい値を挿入するときにインデックスページの分割が少なくなります。

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

3
Mitch Wheat

Entity Frameworkを使用して、Guid(クラスター化および非クラスター化)、Sequential Guid、およびint(Identity/autoincrement)の違いを測定しました。 Sequential Guidは、アイデンティティを備えたintと比較して驚くほど高速でした。 ここに順次ガイドの結果とコード

2
Alex Siepman

OK、私はようやく自分自身で設計と製造のこの時点に到達しました。

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

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

ElasticSearchでは、インデックス作成に関して2つの間に識別可能な違いはありません。コンテンツが時間に関連して供給されるか、またはidフィールドで事前にソートされて[〜#〜] is [〜 #〜]時間に関連し、部分的にシーケンシャルで、スピードアップします。

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

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() );
    }

}

2
Dennis

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

セキュリティの問題が多いようです。この場合は、Sequential Guidを使用しないでください。さらに、UIから渡されたり転送されたりする標準のPKのGuidを使用し、それ以外の場合はすべてのSequence GUIDを使用します。常に絶対的な真実はないので、私はこれを反映するために主な回答も編集しました。

1