ID列のインデックスは非クラスター化する必要がありますか?


19

ID列を持つテーブルの場合、ID列に対してクラスター化または非クラスター化PK /一意のインデックスを作成する必要がありますか?

その理由は、クエリ用に他のインデックスが作成されるためです。非クラスター化インデックス(ヒープ上)を使用し、インデックスでカバーされない列を返すクエリは、余分なクラスター化インデックスBツリーシークステップがないため、使用する論理I / O(LIO)が少なくなりますか?

create table T (
  Id int identity(1,1) primary key, -- clustered or non-clustered? (surrogate key, may be used to join another table)
  A .... -- A, B, C have mixed data type of int, date, varchar, float, money, ....
  B ....
  C ....
  ....)

create index ix_A on T (A)
create index ix_..... -- Many indexes can be created for queries

-- Common query is query on A, B, C, ....
select A, B 
from T 
where A between @a and @a+5 -- This query will have less LIO if the PK is non-clustered (seek)

select A, B, C
from T 
where B between @a and @a+5 

....

次の理由により、ID列のクラスター化PKは優れています

  1. 単調に増加するため、挿入時にページが分割されることはありません。一括挿入は、ヒープ(クラスタ化されていない)テーブルと同じくらい高速になると言われています

  2. 狭いです

ただし、質問のクエリは、クラスター化することなく高速になりますか?

**更新:** Idが他のテーブルのFKであり、いくつかのクエリで結合される場合はどうなりますか?


3
それは良くも悪くもありません、それは依存します。
アーロンバートランド

1
@ypercubeリンクkejser.org/clustered-indexes-vs-heapsは、非CIのLIOが少なくなると述べています。
u23432534

2
私は過去にこの記事を読みましたが、確かにクラスター化インデックスの場合とヒープの場合があることを指摘しています。すべてが黒またはすべて白ではありません。
ypercubeᵀᴹ

4
@ypercubeへの応答が、少なくともKejser氏が引用した基準のいずれかを満たしているかどうかはわかりません-少なくともあなたが共有した詳細については。現在の形では、これが有用な答えを生成するかどうかは実際にはわかりません。なぜなら、あなたが引用したブログ投稿ですでに行われているほぼすべてのシナリオをカバーする必要があるからです。特定のシナリオに関する詳細を提供できる場合は、投稿の知識の一部を適用できます。
-swasheck

2
次のようなものに依存します:a)ワークロード(OLTP?OLAP?など?)、b)テーブルサイズ、c)通常のフォーム、ほんの数例を挙げます。これらの要因の詳細は提供していないため、推奨事項は環境からの推測に基づいています。また、提案しているクエリを(クリアされたバッファで)プロファイリングし、構成ごとに特定のIOプロファイルを取得して、自分で確認しようとしましたか?
-swasheck

回答:


16

デフォルトでは、PKはクラスター化されており、ほとんどの場合、これで問題ありません。ただし、次の質問をする必要があります。

  • PKをクラスター化する必要がありますか?
  • クラスター化インデックスに最適なキーはどの列ですか?

PKとクラスター化インデックスには2つの違いがあります。

  • PKは制約です。PKは行を一意に識別するために使用されますが、ストレージの概念はありません。ただし、既定では(SSMSで)、クラスター化インデックスがまだ存在しない場合、一意のクラスター化インデックスによって適用されます。
  • クラスター化インデックスは、リーフレベルで行データを格納する特別なタイプのインデックスです。つまり、常にカバーしています。すべての列は、キーの一部であるかどうかに関係なく、リーフレベルで保存されます。一意である必要はありません。その場合、クラスター化されたキーにuniquifier(4バイト)が追加されます。

今、私たちは2つの質問になります:

  • テーブル(PK)の行を一意に識別する方法
  • インデックスのリーフレベル(クラスター化インデックス)に保存する方法

次の方法によって異なります。

  • データモデルを設計する
  • データをクエリし、クエリを記述します
  • データを挿入または更新します
  • ...

まず、クラスター化インデックスが必要ですか?一括挿入する場合は、順序付けされていないデータをHEAPに保存する方が効率的です(クラスター内の順序付けされたデータに対して)。RID(行識別子、8バイト)を使用して行を一意に識別し、ページに格納します。

クラスタ化インデックスはランダムな値であってはなりません。リーフレベルのデータは、インデックスキーによって保存および順序付けされます。したがって、断片化やページ分割を回避するために、継続的に成長する必要があります。これがPKで達成できない場合は、別のキーをクラスター化された候補と見なす必要があります。すべての行が最後のリーフページに追加されるため、同一列、連続GUID、または挿入の日付などのクラスター化インデックスは、連続した観点からは問題ありません。一方、一意の識別子はPKとしてのビジネスニーズには役立ちますが、クラスター化しないでください(ランダムに順序付け/生成されます)。

いくつかのデータおよびクエリ分析の後、クラスター化されたPKでキールックアップを行う前に、ほとんど同じインデックスを使用してデータを取得していることがわかった場合、それをクラスター化インデックスと見なすことができますが、データを一意に識別することはできません。

クラスター化インデックスキーは、インデックスを作成するすべての列で構成されます。uniquefier列(4バイト)は、一意の制約がない場合に追加されます(重複する場合は増分値、そうでない場合はnull)。このインデックスキーは、すべての非クラスター化インデックスのリーフレベルで行ごとに1回保存されます。それらのいくつかは、インデックスツリー(Bツリー)のルートレベルとリーフレベルの間の中間レベル(ブランチ)に数回保存されます。キーが大きすぎると、すべての非クラスター化インデックスが大きくなり、より多くのストレージとより多くのIO、CPU、メモリが必要になります。名前+誕生日+国にPKがある場合、このキー良い候補ではありません。クラスタ化インデックスには大きすぎます。NEWSEQUENTIALID()を使用するuniqueidentifierは、シーケンシャルですが、通常はナローキー(16バイト)とは見なされません。

次に、テーブル内の行を一意に識別する方法を見つけたら、PKを追加できます。クエリで使用しないと思われる場合は、クラスタ化しないでください。クエリを実行する必要がある場合は、別の非クラスター化インデックスを作成できます。PKが一意のインデックスを自動的に作成することに注意してください。

非クラスター化インデックスには、常にクラスター化キーが含まれます。ただし、インデックス付き列(+キー列)がカバーしている場合、クラスター化インデックスではキー検索は行われません。また、IncludeおよびWhereを非クラスター化インデックスに追加できることを忘れないでください。(賢く使う)

クラスター化インデックスは一意であり、可能な限り狭くする必要があります。クラスター化インデックスは時間の経過とともに変化せず、増分的に挿入する必要があります。

次に、テーブル、クラスター化および非クラスター化のインデックスと制約を作成するSQLを作成します。

データモデルと使用されているデータ型(AおよびB)がわからないため、これはすべて理論的です。


11

ID列に主キー(PK)があるテーブルの場合、デフォルトでクラスター化されます。クラスタ化されていない方が良いでしょうか?

ID列の主キーのデフォルト(特に)を非クラスター化する必要があるかどうかを尋ねている場合、noと言います。ほとんどのテーブルはクラスター化インデックスを持つことで恩恵を受けるため、特にSQL Serverの新規ユーザーにとっては、クラスター化を主キー制約のデフォルトにすることはおそらく全体的に役立つでしょう。

ほぼすべてのオプションと同様に、一方が他方よりも優先される状況は常に異なりますが、経験豊富なDBAはデフォルトを認識し、必要に応じてデフォルトをオーバーライドできる必要があります。関連するQ&A、主キーを非クラスターとして宣言する必要がある場合も参照してください

質問のクエリは、クラスター化することなく高速になりますか?

はい、ただし注意が必要です。

実際、RIDルックアップはキールックアップよりも効率的です。必要なすべてのページがメモリ内にある場合(インデックスの上位レベルである可能性が非常に高い)でも、クラスター化インデックスbツリーのナビゲートに関連するCPUコストがあります。結果として、SQL Serverは通常、CPU時間の単位ごとにキー検索よりもはるかに多くのRID検索を実行できます。

注意事項

上記のことは、テーブルをヒープとして構築するかどうかを決定する際の決定要因にはなりません。ルックアップ(カバーインデックスを使用)を回避することは非実用的である必要があり、ルックアップの数は、ハードウェア環境とワークロードを考慮して、パフォーマンスに測定可能な(そして重要な)影響を与えるのに十分大きくなければなりません。

この回答では、ヒープとクラスター化インデックスの議論のすべての側面をカバーするのは実際的ではありませんが、一般的にテーブルをヒープとして構成することを好む理由は比較的少ないと思います。私にとって、質問で提案されている種類の設計を選択するには、実装前に非常に慎重な分析が必要であり、高い基準を満たす必要があります。「スケーラビリティ」に関する一般的な議論では十分ではありません。

結合に関する質問の更新に関して、クラスター化インデックスを失うことの実行計画への影響を評価することは、上記の分析の一部を形成します。ネストされたループ結合を使用する場合、行のすべての列はルックアップなしですぐに使用できるため、結合キーにクラスター化インデックスを作成すると非常に便利です。

私自身の経験では、ID列に一意のクラスター化インデックスを作成することは非常に有益であり、すべてが考慮されます。ヒープはスペース管理の点で問題があることがわかりました。また、SQL Serverの機能の中に、機能するために一意のクラスター化インデックスが必要なものあることにも言及する必要があります。


8

実際には、クラスター化インデックスやプライマリキーを作成する必要はありません。一意のインデックスと非一意のインデックスで作業を処理できるためです。SQL Serverは少なくともバージョン1.1以降、クラスター化インデックスをサポートしていますが、主キーは、一意のインデックスを定義することでプログラマーが実施する「概念」にすぎません。

しかし、主キーとクラスター化インデックスの両方は、ほとんどのデータベースで重要な概念のようです。

以下に示すように、SQL Serverのドキュメントを参照して、いくつかのインデックスオプションの部分的な説明を見てみましょう。

クラスター化インデックス: https : //msdn.microsoft.com/en-us/library/ms190457.aspx

  • クラスター化インデックスは、キー値に基づいてデータ行を並べ替えてテーブルまたはビューに格納します。これらは、インデックス定義に含まれる列です。
  • テーブルごとにクラスター化インデックスは1つしか存在できません。

主キー: https : //msdn.microsoft.com/en-us/library/ms190457.aspx

  • テーブルに含めることができるPRIMARY KEY制約は1つだけです。

  • PRIMARY KEY制約内で定義されたすべての列は、NOT NULLとして定義する必要があります。

  • 主キーは、クラスター化インデックス(クラスター化インデックスがない場合の既定)または非クラスター化インデックスとして作成できます。

一意のインデックス: https : //msdn.microsoft.com/en-us/library/ms187019.aspx

  • UNIQUE制約を作成すると、一意の非クラスター化インデックスが作成され、デフォルトでUNIQUE制約が適用されます。

  • テーブルにクラスター化インデックスが存在しない場合は、一意のクラスター化インデックスを指定できます。

つまり、クラスター化インデックスと主キーに関する質問は、実際には次の問題のいくつかに関するものです。すべてのテーブルが同じインデックス作成プランの恩恵を受けるわけではないことに注意してください。

主キーがクラスター化インデックスとは別のものである場合、どのようなメリットがありますか?

おそらく、クラスター化インデックスが広い場合(たとえば、5列のテキスト情報で、主キーが小さい場合(INTまたはBIGINT)、説明しているように見える場合など)。

  • 幅の広いクラスター化インデックスを使用すると、クラスター化インデックステーブルとも呼ばれます)から連続した回答を提供するクエリのサブセットのインデックスから行をすばやく選択できます。たとえば、5列のクラスター化インデックスは、列C1、C2、C3、C4、C5またはC1、C2、C3、C4などをC1までスキャンすることをサポートします。
  • 注:行が大きい場合、特にテーブル内の他の列が結果セットに定期的に含まれている場合、これにより行のシリアルセットを選択する際に速度が向上する可能性があります。
  • その場合、参照整合性のために主キーを使用して、他のテーブルの行を制約する外部キーとして必要な値を提供できます。PKは小さいため、FKは参照されるテーブルのサイズに対する小さなヒットです。
  • ただし、クラスター化インデックスを持つテーブルで作成されたインデックスには、このテーブルで作成した他のインデックスのすべてのクラスター列が含まれることに注意してください。広いクラスター化インデックスは、そのテーブルのすべての非クラスター化インデックスのサイズを拡張します。

主キーのみをクラスター化インデックスにする必要がありますか?

  • 小さな主キー(INTまたはBIGINT)があり、それがクラスター化インデックスである場合、クラスター列のオーバーヘッドは比較的小さくなります。この場合のクラスター化された主キーは、このテーブルのすべてのインデックスにも存在しますが、上記のワイドクラスターよりも安い価格です。

  • この主キークラスター化インデックスは通常、多数の行を連続して選択するための簡単なパスを直接提供しません。

  • クラスター化された主キーを作成したので、クラスター化インデックスに含める予定だった他の列についてはどうでしょうか?

  • 必要に応じて、一意の(または非一意の)インデックスを作成し、列C1、C2、C3、C4、C5の幅広い検索条件にインデックスを付けます。この「模倣クラスター化」インデックスの値は、これらの5つの列の高速検索パスとして機能します。インデックス化されていない列が1つまたは2つあり、それらも定期的に選択されている場合、でインデックスに含めることができます INCLUDE (Doctor_Name, Diagnosis_Synopsis)

単純なクラスター化インデックスと主キーは有用であると思いますが、それらをテーブルで使用するかデータベースで使用するかを考える十分な理由があります。

クラスター化インデックスが必要ですか?

  • インデックス(ユニークインデックスと非ユニークインデックス)を作成し、クラスター化インデックスであるというオーバーヘッドなしで主キーを定義すると、より狭いインデックスがクエリに必要なものを提供する場合があります。

  • クラスター化インデックスと主キーにはいくつかの便利な動作がありますが、最も重要なのは実際にインデックスであることに注意してください。アプリケーションの現実を考慮して、インデックス戦略を設計します。おそらく、OneBigTableほとんどのテーブルで使用しているものとは異なるインデックス作成戦略が必要です。

  • クラスタ化インデックスがない場合、データは行識別子(RID)を含むヒープとして保存されますが、これはまったく適切な検索メカニズムではありません。ただし、前述のように、一意のインデックスと一意でないインデックスを作成して、クエリを処理できます。

これで、ヒープを検討することができます。

ヒープとインデックス: https : //msdn.microsoft.com/en-us/library/hh213609.aspx

  • テーブルがヒープとして格納される場合、個々の行は、ファイル番号、データページ番号、およびページ上のスロットで構成される行識別子(RID)への参照によって識別されます。行IDは小さくて効率的な構造です。(ただし、インデックスではありません。)
  • データが常に非クラスター化インデックスを介してアクセスされ、RIDがクラスター化インデックスキーよりも小さい場合、データアーキテクトはヒープを使用することがあります

ただし、ビッグデータセットに「ホットスポット」がある場合は、別の種類のインデックスを調べることもできます。

フィルター選択されたインデックス: https : //msdn.microsoft.com/en-us/library/cc280372.aspx

  • 適切に設計されたフィルター選択されたインデックスは、フルテーブルの非クラスター化インデックスよりも小さく、フィルターされた統計を持っているため、クエリのパフォーマンスと実行計画の品質が向上します。フィルタリングされた統計は、フィルタリングされたインデックスの行のみをカバーするため、フルテーブル統計よりも正確です。

  • フィルター選択されたインデックスには、フィルター選択されたインデックスへのリンクで概説されている多くの制限があります。

ただし、主キーとクラスター化インデックスを完全にスキップする可能性について考えている場合は、以下にリンクされているMarkus Winandの投稿を読んでください。彼は、いくつかのコードサンプルを使用して、これらの機能の使用を控えるのが良いアイデアであることを示唆する理由を示しています。

http://use-the-index-luke.com/blog/2014-01/unreasonable-defaults-primary-key-clustering-key

しかし最終的には、アプリケーションを理解し、実行中のジョブに合わせてコード、テーブル、インデックスなどを設計することに戻ります。


価値のあることですが、私の日常の作業で、ヒープであるテーブルを見つけた場合、エラーである可能性が高いと考え、意図的にヒープになったかどうかを開発者に確認します。
RLF

-2

考慮すべき点がいくつかあります。

単調に増加する値のインデックス(クラスター化されているかどうかに関係なく)は、一括挿入中のページ分割を節約しますが、インデックスの末尾に新しいホットスポットを作成します。シングルスレッドの一括挿入では問題になりませんが、インデックスの最後のページへのアクセスをスレッドが常に競合するため、新しいタプルを高速で挿入するマルチスレッドアプリケーションの競合が確実に増加します。

サロゲート(ID)PKに基づいてテーブルをクラスタリングすることは、ほとんど有益ではありません。このような主キーは、個々のタプルに一度に1つずつアクセスするか、結合のためにインデックス全体をスキャンするために主に使用されます。どちらの場合でも、インデックスがクラスター化されているかどうかは関係ありません(マージ結合を除き、可能性はありますが、頻度はどれくらいですか?)

キー範囲のスキャンを要求するクエリと、他の列を参照する追加の述語をカバーするクラスター化インデックスの恩恵を最も受けると思います。


これが実際に問題になるには、どれくらいのレートが必要ですか?
ypercubeᵀᴹ

@ypercubeは「依存する」と言えますか?そうだから。テーブルにトリガーがなければ、1秒あたり合計1Kの挿入を行う12個のスレッドで競合が発生し始めると予想されます。
ムスタッチョ


私は同意しませんが、1つのホットスポットでどこまで行けるかを尋ねていました。CIとしてIDENTITYを持つテーブルに1秒あたり3万行を挿入することに関する記事を見たことを覚えていますが(メモリが十分に役立つ場合)、ブログの投稿を見つけることができません。
ypercubeᵀᴹ

特定のハードウェア上の具体的なスキーマに対して実行される具体的なワークロードがない場合、この議論は無意味です。単調に増加するシーケンスのインデックスが「ホットスポット」を作成することに全員が同意できることを願っています。容認できないボトルネックが発生するかどうか、それを気にする必要があるかどうかは、状況によって異なります。
ムスタッチョ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.