クラスター化と非クラスター化


98

私のSQL(Server 2008)に関する低レベルの知識は限られており、現在DBAによって挑戦されています。説明させてください(私は私が正しいことを期待して明白な発言を述べましたが、何か問題を見つけた場合は教えてください)シナリオ:

人のための「裁判所命令」を保持するテーブルがあります。テーブル(名前:CourtOrder)を作成したとき、次のように作成しました。

CREATE TABLE dbo.CourtOrder
(
  CourtOrderID INT NOT NULL IDENTITY(1,1), (Primary Key)
  PersonId INT NOT NULL,
  + around 20 other fields of different types.
)

次に、非効率的なインデックスを主キーに適用しました(効率を高めるため)。私の理由は、それが一意のフィールド(主キー)であり、主に選択目的のためにインデックス付けする必要があることです。Select from table where primary key = ...

次に、CLUSTEREDインデックスをPersonIdに適用しました。その理由は、作業の大部分が人の注文を得ているため、特定の人の注文を物理的にグループ化するためでした。そう、select from mytable where personId = ...

私は今これに引っ張られています。クラスタ化インデックスを主キーに配置し、通常のインデックスをpersonIdに配置する必要があると言われました。それは私には非常に奇妙に思えます。まず、クラスター化インデックスを一意の列に配置するのはなぜですか。それはクラスタリングとは何ですか?確かに、それはクラスタ化インデックスの無駄です。通常のインデックスが一意の列で使用されると思っていました。また、インデックスをクラスター化すると、別の列をクラスター化できなくなります(テーブルごとに1つですよね?)。

私が誤りを犯したと言われる理由は、クラスター化されたインデックスをPersonIdに置くと挿入が遅くなると彼らが信じているからです。選択の速度が5%向上すると、挿入と更新の速度が95%低下します。それは正しいですか?

personIdをクラスター化するため、PersonIdを挿入または変更するときは常にSQL Serverがデータを再配置する必要があると彼らは言います。

それで私は尋ねました、もしそれがとても遅いのになぜSQLはCLUSTERED INDEXの概念を持っているのでしょうか?彼らが言っているのと同じくらい遅いですか?最適なパフォーマンスを実現するには、どのようにインデックスを設定する必要がありますか?私はSELECTがIN​​SERTよりも使用されていると思っていました...しかし、彼らはINSERTSでロックの問題があると言っています...

誰かが私を助けてくれることを願っています。


回答:


117

クラスタ化インデックスと非クラスタ化インデックスの違いは、クラスタ化インデックスがデータベース内の行の物理的な順序を決定することです。言い換えると、クラスター化インデックスを適用PersonIdするPersonIdと、テーブル内で行が物理的に並べ替えられ、これによりインデックス検索で行に直接移動できます(非クラスター化インデックスではなく、行の場所、追加のステップ)。

とはいえ、主キーがクラスタ化インデックスではないことは珍しくありませんが、前例はありません。シナリオの問題は、実際には想定しているものの逆です。重複ではなく、クラスター化インデックスに一意の値が必要です。クラスタ化インデックスは行の物理的な順序を決定するため、インデックスが一意でない列にある場合、サーバーは重複するキー値を持つ行にバックグラウンド値を追加する必要があります(この場合、同じPersonId)組み合わせた値(キー+バックグラウンド値)が一意になるようにします。

私は示唆している唯一のことはされていないサロゲートキー(自分の使用してCourtOrderId主キーとして)のカラムを、代わりの複合主キーを使用PersonIdし、他のいくつかのユニークな識別列または列のセットを。ただし、それができない(または実用的でない)場合は、クラスター化インデックスをに配置しCourtOrderIdます。


アダムに感謝します。では、クラスタ化インデックスはいつ役立つのでしょうか。たとえば、ほとんどのクエリがPersonIDにあるような場合に、クラスター化インデックスがデータをグループ化することの利点だと思いました...データがグループ化されます。
クレイグ、

3
それはされていない物理的に並べ替えPersonId。これはによって論理的にソートされPersonIdます。論理的順序と物理的順序の不一致は、論理断片化の程度です。
マーティン・スミス

1
@cdotlisterインデックスの利点は、データをグループ化するのではなく、データをソートすることです(これは、インデックス内の重複データを意味します)。区別は意味的に見えるかもしれませんが、クラスター化インデックスの場合はそうではありません。可能であれば、クラスター化インデックスは行を一意に識別するものでなければならず、(理想的には)最も一般的に照会される列または列のセットでもあります。これが通常は主キーにある理由です。
Adam Robinson

1
@Cyber​​SluethOmega:わかりません。あなたの質問には、私が決定を下すのに十分な情報が含まれていません。テーブルの最後以外に行が頻繁に追加または削除される列のセットにクラスター化インデックスが必要ですか?いいえ。しかし、なぜあなたがそれを尋ねているのか、あるいはなぜ反対票を投じているのか、私にはよくわかりません。
アダムロビンソン

1
@Cyber​​SluethOmega:コメントが意図されていない場合、インターネットではコメントが防御的または冷たく聞こえる場合があります。クラスター化インデックスを主キー以外のものにする状況は知らないと言ったとおっしゃっていましたが、実際にはそのようなことはありませんでした。実際には、私が言ったことだった「これは珍しい ...が、前代未聞のではない」、その私はことを意味しない、これが行われている例を知っています。
アダムロビンソン

14

私は決してSQLエキスパートではありません... DBAの見解ではなく、開発者の見解としてこれを取り上げてください。

クラスター化された(物理的に順序付けられた)インデックスへの挿入が順番になっていないと、挿入/更新に余分な作業が発生します。また、一度に多くの挿入が発生し、それらがすべて同じ場所で発生している場合、競合が発生します。特定のパフォーマンスは、データとデータへのアクセス方法によって異なります。一般的な経験則は、テーブル内の最もユニークなナロー値(通常はPK)にクラスター化インデックスを構築することです

私はあなたのPersonIdが変更されないことを想定しているので、更新はここでは機能しません。ただし、PersonIdが1 2 3 3 4 5 6 7 8 8の数行のスナップショットを考えてみます。

ここで、PersonIdが3の新しい行を20行挿入します。最初に、これは一意のキーではないため、サーバーは値に(シーンの背後で)値を追加して一意にし(さらにスペースを追加します)、次に場所を追加します。これらは変更する必要があります。これを、最後に挿入が行われる自動インクリメントPKの挿入と比較してください。非技術的な説明はおそらくこれに帰着します。アイテムの挿入時にテーブルの最後で既存のアイテムの場所を再調整するのではなく、テーブルの最後で自然に高い値に進んでいる場合、実行する「リーフシャッフル」作業が少なくなります。

さて、挿入に問題がある場合は、同じ(または類似の)PersonId値の束を一度に挿入している可能性が高く、これにより、テーブル全体のさまざまな場所でこの余分な作業が発生し、断片化によって殺されています。ケースでクラスター化されているPKに切り替えることのマイナス面は、テーブル全体で値のばらつきが大きいPersonIdで今日挿入の問題が発生している場合、クラスター化されたインデックスをPKに切り替え、すべての挿入が1つで行われる場合です。その場合、競合の集中が高まるため、問題が実際に悪化する可能性があります。(反対に、今日の挿入が全体に広がっていないが、通常はすべて同じような領域にまとめられている場合、クラスター化されたインデックスをPersonIdからPKに切り替えることで問題がおそらく緩和されます。断片化。)

パフォーマンスの問題は、固有の状況に合わせて分析し、これらのタイプの回答を一般的なガイドラインとしてのみ使用する必要があります。あなたの最善の策は、問題のある場所を正確に検証できるDBAに頼ることです。単純なインデックスの調整を超える可能性があるリソース競合の問題があるようです。これは、はるかに大きな問題の症状である可能性があります。(おそらく設計上の問題...そうでなければリソースの制限。)

とにかく頑張ってね!


5

一部の作成者は、範囲クエリにメリットがある代替案がある場合CIidentity列を「無駄にする」ことを推奨しません。

MSDN Clustered Index Designガイドラインから、次の基準に従ってキーを選択する必要があります

  1. 頻繁に使用されるクエリに使用できます。
  2. 高度な一意性を提供します。
  3. 範囲クエリで使用できます。

あなたのCourtOrderIDコラムは会い2ます。あなたはPersonId満たしている13uniqueifierとにかくほとんどの行が追加さPersonId,CourtOrderIDれてしまうので、これを一意として宣言して使用することもできます。これは同じ幅になるためですが、クラスター化インデックスキーが行ロケーターとしてすべてのNCIに追加され、これにより、より多くのクエリをカバーします。

PersonId,CourtOrderIDCIとして使用する場合の主な問題は、論理的な断片化が発生する可能性が高いことであり(これは特に、支援しようとしている範囲クエリに影響します)、フィルファクターと断片化レベルを監視し、インデックスのメンテナンスをより頻繁に実行する必要があります。


3

次のリンクで説明されています:https : //msdn.microsoft.com/en-us/ms190457.aspx

クラスター化

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

  • テーブル内のデータ行が並べ替えられた順序で格納されるのは、テーブルにクラスター化インデックスが含まれている場合のみです。テーブルにクラスター化インデックスがある場合、そのテーブルはクラスター化テーブルと呼ばれます。テーブルにクラスター化インデックスがない場合、そのデータ行はヒープと呼ばれる順序付けられていない構造に格納されます。

非クラスター化

  • 非クラスター化インデックスは、データ行とは別の構造を持っています。非クラスター化インデックスには非クラスター化インデックスのキー値が含まれ、各キー値のエントリには、キー値を含むデータ行へのポインターがあります

  • 非クラスター化インデックスのインデックス行からデータ行へのポインターは、行ロケーターと呼ばれます。行ロケーターの構造は、データページがヒープに格納されているか、クラスター化されたテーブルに格納されているかによって異なります。ヒープの場合、行ロケータは行へのポインタです。クラスタ化テーブルの場合、行ロケータはクラスタ化インデックスキーです。

  • 非クラスター化インデックスのリーフレベルに非キー列を追加して、既存のインデックスキー制限である900バイトと16個のキー列をバイパスし、完全にカバーされたインデックス付きクエリを実行できます。


-3

いくつかの厄介な選択、ストアドプロシージャでの結合を伴ういくつかのデータベース-違いはインデックスのみです

INDEXES-クラスター化と非クラスター化

  891 rows
  10 sec
  NONCLUSTERED 

  OR

  891 rows
  14 sec
  CLUSTERED
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.