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 = ...
次に、PersonIdにCLUSTEREDインデックスを適用しました。その理由は、特定の人の注文を物理的にグループ化することでした。仕事の大部分は人の注文を取得しているからです。したがって、select from mytable where personId = ...
私は今、これについて引き上げられました。主キーにクラスター化インデックスを配置し、personIdに通常のインデックスを配置する必要があると言われました。それは私には非常に奇妙に思えます。まず、クラスター化インデックスを一意の列に配置するのはなぜですか?クラスタリングとは何ですか?確かにそれはクラスター化インデックスの無駄でしょうか?ユニークな列では通常のインデックスが使用されると信じていました。また、インデックスをクラスター化すると、別の列をクラスター化できなくなります(テーブルごとに1つです?)。
間違いを犯したと言われた理由は、PersonIdにクラスター化インデックスを配置すると挿入が遅くなると考えているからです。選択の速度が5%向上すると、挿入と更新の速度が95%低下します。それは正しいですか?
PersonIdをクラスター化するため、SQL ServerはPersonIdを挿入または変更するたびにデータを再配置する必要があります。
それで、私は、もしそれが非常に遅いなら、なぜSQLにクラスター化インデックスの概念があるのかと尋ねました。彼らが言っているほど遅いですか?最適なパフォーマンスを実現するには、どのようにインデックスを設定すればよいですか? SELECTはINSERTよりも多く使用されると思っていましたが、INSERTでロックの問題が発生していると彼らは言います...
誰かが私を助けてくれることを願っています。
クラスタ化インデックスと非クラスタ化インデックスの違いは、クラスタ化インデックスがデータベース内の行の物理的順序を決定することです。つまり、クラスター化インデックスをPersonId
に適用すると、テーブル内のPersonId
によって行が物理的に並べ替えられ、これに対するインデックス検索が(非-クラスター化インデックス。行の場所に移動し、追加の手順を追加します)。
つまり、主キーがクラスター化インデックスではなく、前代未聞ではないことはunusualです。シナリオの問題は、実際には想定していることの反対です。重複ではなくクラスター化インデックスにunique値が必要です。クラスター化インデックスは行の物理的な順序を決定するため、インデックスが一意でない列にある場合、サーバーは重複するキー値を持つ行にバックグラウンド値を追加する必要があります(この場合、同じ行PersonId
)。これにより、結合値(キー+バックグラウンド値)が一意になります。
私が提案する唯一のものは、主キーとして代理キー(CourtOrderId
)列を使用するnotが、代わりに複合主キーを使用することですPersonId
およびその他の一意に識別する列または列のセットのキー。ただし、それが不可能な場合(または実用的でない場合)は、CourtOrderId
にクラスター化インデックスを配置します。
私は決してSQLエキスパートではないので、これをDBAビューではなく開発者のビューと考えてください。
連続した順序ではないクラスター化された(物理的に順序付けられた)インデックスへの挿入は、挿入/更新に余分な作業を引き起こします。また、一度に多くの挿入が発生し、それらがすべて同じ場所で発生する場合、競合が発生します。特定のパフォーマンスは、データとそのアクセス方法によって異なります。一般的な経験則は、テーブル内の最も一意の狭い値(通常はPK)にクラスター化インデックスを構築することです。
PersonIdは変更されないと想定しているため、ここでは更新プログラムは関係ありません。しかし、1 2 3 3 4 5 6 7 8 8のPersonIdを持ついくつかの行のスナップショットを検討してください。
ここで、PersonIdが3の新しい行を20行挿入します。最初に、これは一意のキーではないため、サーバーは値に(シーンの背後で)余分なバイトを追加して一意にし(さらにスペースを追加します)、次にこれらは常駐しなければなりません。最後に挿入が発生する自動インクリメントPKの挿入と比較してください。技術的でない説明はこれに帰着する可能性があります:アイテムを挿入する間にその場所で既存のアイテムの場所を再加工するよりも、テーブルの終わりでより高い値が自然に進んでいる場合、行うべき「リーフシャッフル」作業が少なくなります。
現在、挿入に問題がある場合は、同じ(または類似の)PersonId値の束を一度に挿入する可能性があり、これがテーブル全体のさまざまな場所でこの余分な作業を引き起こし、断片化があなたを殺しています。あなたのケースでクラスタ化されているPKに切り替えることのマイナス面は、テーブル全体で値のばらつきがあるPersonIdで挿入問題が発生している場合、クラスタ化インデックスをPKに切り替え、すべての挿入が1回で発生する場合です場所を指定すると、競合の集中度が高まるため、実際に問題が悪化する可能性があります。 (逆に、今日の挿入物が全体に散らばっていなくても、通常はすべて同じような領域にまとめられている場合、クラスター化インデックスをPersonIdからPKに切り替えることで問題が緩和される可能性が高くなります。フラグメンテーション。)
パフォーマンスの問題は、独自の状況に合わせて分析し、これらのタイプの回答を一般的なガイドラインとしてのみ使用する必要があります。最善の策は、問題のある場所を正確に検証できるDBAに頼ることです。単純なインデックスの調整を超えたリソース競合の問題があるようです。これは、はるかに大きな問題の症状である可能性があります。 (同様に設計の問題...そうでなければリソースの制限。)
いずれにせよ、幸運を祈ります!
一部の著者は、範囲クエリに役立つ代替手段がある場合、CI
列のidentity
を「無駄にしない」ことを提案しています。
MSDNから Clustered Index Design Guidelines キーは次の基準に従って選択する必要があります
CourtOrderID
列は2
に適合しています。 PersonId
は1
および3
に適合しています。とにかくほとんどの行はuniqueifier
が追加されるので、それを一意として宣言し、PersonId,CourtOrderID
を使用することもできます。これは同じ幅ですが、クラスター化インデックスキーが追加されるとより便利になるためすべてのNCIが行ロケーターとして機能するため、より多くのクエリをカバーできます。
CIとしてPersonId,CourtOrderID
を使用する場合の主な問題は、論理的な断片化が発生する可能性が高いことです(これは特に、支援しようとしている範囲クエリに影響します)。より頻繁に。
次のリンクで説明されています: https://msdn.Microsoft.com/en-us/ms190457.aspx
クラスター化
クラスター化インデックスは、キー値に基づいてデータ行をテーブルまたはビューにソートおよび格納します。これらは、インデックス定義に含まれる列です。データ行自体は1つの順序でのみソートできるため、テーブルごとにクラスター化インデックスは1つしか存在できません。
テーブルのデータ行がソートされた順序で保存されるのは、テーブルにクラスター化インデックスが含まれている場合のみです。テーブルにクラスター化インデックスがある場合、そのテーブルはクラスター化テーブルと呼ばれます。テーブルにクラスター化インデックスがない場合、そのデータ行はヒープと呼ばれる順序付けられていない構造に格納されます。
非クラスター化
非クラスター化インデックスには、データ行とは別の構造があります。非クラスター化インデックスcには非クラスター化インデックスキー値が含まれ、各キー値エントリにはキー値を含むデータ行へのポインターがあります。
非クラスター化インデックスのインデックス行からデータ行へのポインターは、行ロケーターと呼ばれます。行ロケーターの構造は、データページがヒープまたはクラスターテーブルのどちらに格納されているかによって異なります。ヒープの場合、行ロケーターは行へのポインターです。クラスター化テーブルの場合、行ロケーターはクラスター化インデックスキーです。
非クラスター化インデックスのリーフレベルに非キー列を追加して、既存のインデックスキー制限である900バイトと16キー列をバイパスし、完全にカバーされたインデックス付きクエリを実行できます。