web-dev-qa-db-ja.com

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

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

その理由は、クエリに対して他のインデックスが作成されるためです。 (ヒープ上の)非クラスター化インデックスを使用し、インデックスでカバーされていない列を返すクエリは、余分なクラスター化インデックスの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であり、いくつかのクエリで結合される場合はどうなりますか?

19
u23432534

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

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

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

  • PKは制約です。 PKは行を一意に識別するために使用されますが、ストレージの概念はありません。ただし、既定では(SSMSの場合)、クラスター化インデックスがまだ存在しない場合は、一意のクラスター化インデックスによって適用されます。
  • クラスター化インデックスは、行データをリーフレベルで格納する特別なタイプのインデックスであり、常にカバーしています。キーの一部であるかどうかに関係なく、すべての列はリーフレベルで格納されます。一意である必要はありません。その場合、一意化記号(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)がわからないため、これはすべて理論的です。

16

ID列に主キー(PK)を持つテーブルの場合、デフォルトでクラスター化されます。非クラスター化した方が良いですか?

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

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

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

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

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

注意事項

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

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

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

私自身の経験では、ID列に一意のクラスター化インデックスを付けることは非常に多くの場合有益であり、すべてを考慮しています。領域管理の点でヒープに問題があることを発見しました。また、SQL Serverの一部の機能が機能するために一意のクラスター化インデックスを必要とすることにも言及する必要があります。

11
Paul White 9

実際、一意のインデックスと非一意のインデックスで処理できるため、クラスター化インデックスや主キーを作成する必要はありません。 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制約が適用されます。

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

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

主キーがクラスター化インデックスから分離されていると、いつメリットが得られますか?

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

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

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

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

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

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

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

単純なクラスター化インデックスと主キーは便利だと思いますが、テーブルで使用するかデータベースで使用するかを検討する理由はいくつかあります。

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

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

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

  • クラスタ化インデックスがないと、データはheapとして保存され、行識別子(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

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

8
RLF