SQL Server 2012でのPK GUIDのインデックス作成


13

私の開発者は、ほとんどすべてのテーブルでGUIDをPKとして使用するようにアプリケーションを設定しており、デフォルトでSQL ServerはこれらのPKでクラスター化インデックスを設定しています。

システムは比較的新しく、最大のテーブルは100万行を超えていますが、インデックス作成を検討しており、近い将来必要に応じて迅速にスケーリングできるようにしたいと考えています。

したがって、私の最初の傾向は、クラスター化インデックスを、DateTimeのbigint表現である作成済みフィールドに移動することでした。ただし、CXを一意にできる唯一の方法は、このCXにGUID列を含めることです。ただし、最初に作成されます。

これにより、クラスタリングキーの幅が広がりすぎて、書き込みのパフォーマンスが向上しますか?読み取りも重要ですが、書き込みはおそらくこの時点で大きな懸念事項です。


1
GUIDはどのように生成されますか?NEWIDまたはNEWSEQUENTIALID?
swasheck

6
クラスター化されたGUIDと挿入のパフォーマンスは、「パフォーマンス」の直前の単語が最小化されている場合にのみ文に含める必要があります
billinkc

2
これらの開発者を昼食に連れ出し、NEWID()を主キーとして再び使用すると、パフォーマンスの低下を非難することを説明します。彼らはそれを防ぐために何をすべきかを非常に素早く尋ねます。その時点で、代わりにIDENTITY(1,1)を使用すると言います。(おそらく少し単純化しすぎていますが、10回のうち9回は動作します)。
マックスヴァーノン

3
GUIDに対する憎しみの理由は、それらがワイド(16バイト)であり、作成されない場合newsequentialidはランダムであるということです。クラスタ化キーは、幅が狭く増加している場合に最適です。GUIDは反対です:脂肪とランダム。本でいっぱいの本棚を想像してみてください。OEDが入ってくると、GUIDがランダムであるため、シェルフの中央に挿入されます。物事を整理するために、本の右半分は時間のかかるタスクである新しい場所に突き刺さなければなりません。それがGUIDがデータベースに対して行っていることであり、パフォーマンスを低下させます。
billinkc

7
uniqueidentifiersを使用する問題を解決する方法は、uniqueidentifiersを使用せずに図面に戻ることです。システムが小さい場合、それらはひどいものではありませんが、少なくとも数百万行以上のテーブル(またはそれより大きいテーブル)がある場合、キーのuniqueidentifiersを使用して押しつぶされます。
ジョンセイゲル

回答:


20

GUIDの主な問題、特に非順次的な問題は次のとおりです。

  • キーのサイズ(16バイト対INTの4バイト):これは、クラスター化インデックスの場合、インデックスの追加スペースとともに4倍の量のデータをキーに格納することを意味します。
  • インデックスの断片化:キー値の完全にランダムな性質のため、非シーケンシャルGUID列を最適化しておくことは事実上不可能です。

それであなたの状況にとってこれはどういう意味ですか?それはあなたの設計次第です。システムが単に書き込みに関するものであり、データの取得について懸念がない場合、Thomas Kによって概説されたアプローチは正確です。ただし、この戦略を追求することにより、そのデータを読み取って保存するための多くの潜在的な問題を作成していることに留意する必要があります。ジョン・シーゲルが指摘する、あなたはまた、より多くのスペースを占有し、基本的にメモリの肥大化を持つことになります。

GUIDに関する主な質問は、GUIDの必要性です。開発者は、グローバルな一意性を保証するためにそれらを好みますが、この種の一意性が必要になることはまれです。ただし、値の最大数が2,147,483,647(4バイトの符号付き整数の最大値)より小さい場合は、おそらくキーに適切なデータ型を使用していないことを考慮してください。BIGINT(8バイト)を使用しても、最大値は9,223,372,036,854,775,807です。通常、一意のキーに自動インクリメント値が必要な場合、これは非グローバルデータベース(および多くのグローバルデータベース)に十分です。

最後に、クラスターインデックスに対してヒープを使用する場合、純粋にデータを書き込む場合、挿入のオーバーヘッドを最小限に抑えるため、ヒープが最も効率的です。ただし、SQL Serverのヒープは、データの取得には非常に非効率的です。私の経験では、クラスター化インデックスは、宣言する機会がある場合に常に望ましいものです。テーブルにクラスター化インデックスを追加すると(40億以上のレコード)、全体的な選択パフォーマンスが6倍に向上します。

追加情報:


13

OLTPシステムのキーおよびクラスターとしてのGUIDには何の問題もありません(クラスターのサイズの増加に悩まされるインデックスがテーブルにたくさんある場合を除く)。実際、IDENTITY列よりもはるかにスケーラブルです。

GUIDはSQL Serverの大きな問題であると広く信じられています。主に、これはまったく間違っています。実際、GUIDは、約8コア以上のボックスで大幅に拡張可能です。

申し訳ありませんが、開発者は正しいです。GUIDを心配する前に、他のことを心配してください。

ああ、最後に:そもそもなぜクラスターインデックスが必要なのですか?懸念事項が多数の小さなインデックスを持つOLTPシステムである場合は、ヒープを使用する方が適切です。

次に、GUIDが導入する断片化が読み取りにどのような影響を与えるかを考えてみましょう。フラグメンテーションには3つの大きな問題があります。

  1. ページ分割にはディスクI / Oがかかります
  2. 半分のフルページは、フルページほどメモリ効率が良くない
  3. ページが順番どおりに保存されないため、シーケンシャルI / Oの可能性が低くなります

質問での懸念はスケーラビリティに関するものであるため、「ハードウェアを追加するとシステムが高速化する」と定義できるため、これらは最小の問題です。それぞれに順番に対処するには

広告1)スケールが必要な場合は、I / Oを購入する余裕があります。安価なSamsung / Intel 512GB SSD(数米ドル/ GB)でも、10万IOPSをはるかに超えます。2ソケットシステムですぐに消費することはありません。そして、あなたがそれに出くわすなら、もう1つを買うとあなたは設定されています

広告2)テーブルで削除を行うと、とにかくページが半分になります。たとえそうでなくても、メモリは安価であり、最大のOLTPシステムを除くすべてのシステムにとって、ホットデータはそこに収まるはずです。より多くのデータをページにパックすることは、スケールを探しているときに最適化されていません。

広告3)頻繁にページ分割され、非常に断片化されたデータで構築されたテーブルは、連続して入力されたテーブルとまったく同じ速度でランダムI / Oを実行します

参加に関しては、OLTPのようなワークロードのような2つの主要な参加タイプがあります。ハッシュとループです。それぞれを順番に見てみましょう。

ハッシュ結合:ハッシュ結合は、小さなテーブルがスキャンされ、通常は大きなテーブルが検索されることを前提としています。小さなテーブルはメモリ内にある可能性が非常に高いため、ここではI / Oを心配する必要はありません。シークは、断片化されたインデックスと非断片化されたインデックスのコストが同じであるという事実に既に触れました。

ループ結合:外部テーブルが検索されます。同じ費用

また、多くの不適切なテーブルスキャンが実行されている可能性がありますが、GUIDは再び問題ではなく、適切なインデックス作成が重要です。

現在、正当な範囲スキャンが行われている場合があり(特に外部キーを結合する場合)、この場合、断片化されていないデータに比べて断片化されたデータの「パック」が少なくなります。しかし、3NFデータが適切にインデックス付けされている場合に表示される可能性のある結合は次のとおりです。

  1. 参照するテーブルの主キーへの外部キー参照を持つテーブルからの結合

  2. 逆に

広告1)この場合、主キーへの1回のシークを行います-nを1に結合します。断片化の有無、同じコスト(1シーク)

広告2)この場合、同じキーに参加していますが、複数の行を取得できます(範囲シーク)。この場合の結合は1対nです。ただし、探している外部テーブルは、同じキーを探しています。これは、断片化されていないインデックスと同じように、断片化されたインデックスの同じページにある可能性があります。

これらの外部キーについて少し考えてみましょう。主キーを「完全に」シーケンシャルに配置した場合でも、そのキーを指すものはすべて非シーケンシャルです。

もちろん、お金が安くてプロセスが高い銀行のあるSANの仮想マシンで実行している場合があります。その後、このアドバイスはすべて失われます。しかし、それがあなたの世界である場合、スケーラビリティはおそらくあなたが探しているものではありません-あなたはパフォーマンスと高速/コストを探しています-これらは両方とも異なっています。


1
コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
ポールホワイト9

5

トーマス:あなたのポイントのいくつかは完全に理にかなっており、私はそれらすべてに同意します。SSDを使用している場合、最適化対象のバランスが変わります。ランダムvsシーケンシャルは、回転ディスクと同じ議論ではありません。

私は特に、純粋なDBビューをとることは恐ろしく間違っていることに同意します。DBのパフォーマンスのみを向上させるためにアプリケーションを低速かつスケーラブルにしないことは、非常に誤った方向に導かれる可能性があります。

IDENTITY(またはシーケンス、またはDBで生成されるもの)の大きな問題は、キーを作成するためにDBへのラウンドトリップが必要になるためひどく遅いことです。これにより、DBでボトルネックが自動的に発生し、アプリケーションがDB呼び出しを行って、キーの使用を開始します。GUIDを作成すると、アプリケーションを使用してキーを作成することでこれが解決され、(定義により)グローバルに一意であることが保証されます。したがって、アプリケーションレイヤーはそれを使用して、DBラウンドトリップが発生する前にレコードを渡すことができます。

ただし、GUIDの代わりに使用する傾向があります。ここでのデータ型に対する個人的な好みは、アプリによって生成されるグローバルに一意のBIGINTです。これをどのように行うのですか?最も簡単な例では、GUIDをハッシュするために、非常に軽量な小さな関数をアプリに追加します。ハッシュ関数が高速で比較的高速であると仮定します(1つの例については、GoogleのCityHashを参照してくださいhttp : //google-opensource.blogspot.in/2011/04/introducing-cityhash.html-すべてのコンパイル手順が正しいことを確認し、または単純なコードの場合はhttp://tools.ietf.org/html/draft-eastlake-fnv-03のFNV1aバリアント)これにより、アプリケーションで生成された一意の識別子と、CPUがよりよく機能する64ビットキー値の両方の利点が得られます。

BIGINTを生成する他の方法があり、これらの両方のアルゴリズムでは、ハッシュ衝突の可能性があります-読んで、意識的な決定をします。


2
トーマスの答えに対する答えとしてではなく、OPの質問に対する答えとしてあなたの答えを編集することをお勧めします。それでも、Thomas(MikeFalの)とあなたの提案の違いを強調することができます。
ypercubeᵀᴹ

2
質問への回答をお願いします。そうでない場合は削除します。
JNK

2
コメントをありがとうマーク。回答を編集するとき(非常に優れたコンテキストを提供すると思います)、私は1つのことを変更します。INSERTに注意すれば、IDENTITYはサーバーへの追加のラウンドトリップを必要としません。あなたはいつも.. INSERTを呼び出すバッチでSCOPE_IDENTITY()を返すことができます
トーマスKejser

1
「キーを作成するためにデータベースへの往復が必要なため、恐ろしく遅い」-1回の往復で必要な数を取得できます。
AK 14

「1回のラウンドトリップで必要な数だけ取得できます」について-IDENTITY列や、データベースレベルで基本的にDEFAULTを使用している他の方法ではこれを実行できません。
アヴィチェリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.