SQL Server:すべての列を含むインデックスをカバーしていますか?


9

私たちのチームは、アプリケーションと関連するデータベースを継承しています。以前の開発者は、すべてのテーブルのすべてのインデックスにINCLUDE句があり、それ以外の場合はキーの一部ではないすべての列を常に追加するというルールを適用しているようです。これらのテーブルには、平均して2〜5個のインデックスまたは一意の制約と外部キーがあります。

アクセスはデフォルトで(常にではないが)すべての列を取得するORMを介して行われるため、データベースでスローされるクエリに関係なく、SELECTのパフォーマンスを向上させることを目的としています。これの副作用は、ストレージ要件の増加(おそらく大幅に増加する)とINSERT / UPDATE / DELETEの追加のオーバーヘッド時間であると予想されます。

問題は、これは賢明な戦略ですか?私たちのチームにはSQL Serverの履歴がありますが、内部の動作について専門家であると考えるメンバーはいません(ただし、この戦略が最適だった場合、今のところデフォルトではないのではないかという質問が出されました)。他にどのような副作用(データベースサーバーのCPU /メモリ/ TempDBの使用など)が予想されますか、または上記の仮定の一部が正しくありませんか?

さらに、アプリケーションは、オンプレミスのSQL Server(2012年以降のバージョン)とAzure SQLの両方にインストールできます-この結果として、2つの違い、またはAzureへの追加の副作用に備えておく必要があります。アプローチ?

回答:


8

これまで、特定のインデックスに対してこれを実行して、頻繁に実行される大量のクエリを支援しました。実際に彼らが行ったことは、複数のクラスター化インデックスを作成することです。これらのインデックスのいずれかを使用して行を検索する場合、実際のクラスター化インデックス(または実際のクラスター化インデックスがない場合はヒープ)の残りのデータを検索する追加の作業は必要ありません。 。

これは賢明な戦略ですか?

特定のクエリパターンをサポートする必要がある一部のインデックスでは、確かにそうです。

しかし、すべてのインデックスでこれを行うために、私は確かにノーと言うでしょう。

実際に必要のない場所で行うにはスペースが無駄になり、挿入/更新が大幅に遅くなります。各インデックスページが保持するレコードが少なくなるため、フィルター処理のためにインデックスのチャンクを参照する必要があるが、他のすべての列を使用する必要がないクエリは、より多くのページにアクセスする必要があるため、読み取りクエリが遅くなる可能性があります。これにより、データベースのメモリ消費量が増加します。これらのページはバッファプールにロードする必要があり、メモリが不足している場合、他の有用なページを排出する可能性があります。これらのインデックスで圧縮を使用して、ストレージとメモリの要件への影響を軽減しようとすると、代わりにCPUに余分な負荷がかかります。

デフォルトでは(常にではないが)すべての列を取得するORMを介してアクセスするため

これはORM(または単純なORM)の最適化が不十分な一般的なパターンであり、これらの場合、SQL Serverのインデックスアドバイザー(および同様のサードパーティツール)が多数のINCLUDEd列を持つインデックスを提案するので、あなたの意見に同意しますこれが、このようにしてインデックスが作成された理由です。

しかし、そのようなクエリはすべて少し速くなり、一部は大幅に速くなる可能性がありますが、多くの場合、メリットは非常に小さいため、共通のワーキングセット、ディスク上のスペースに必要な追加のメモリフットプリントに値しないと思いますディスクとメモリ間のIO。

また、ORMは、クエリが関連するすべてのテーブルのすべての列を選択していない可能性があるため、現在のリクエストのメインターゲットにのみメリットがあり、他のオブジェクトがフィルタリングに使用されている場合、インデックスが大きいとクエリにペナルティが科される可能性があることにも注意してください。データを返さない(SELECT * FROM table1 WHERE id IN (SELECT someID FROM table2 WHERE someColumn='DesiredValue')おそらく)。

特にデータが大きい場合に使用される余分なスペースについてのもう1つの考慮事項は、バックアップ戦略に影響を与えることです。つまり、これらのバックアップのストレージと転送のコスト、潜在的な復元時間などです。

2つの[オンプレミスとAzureSQL]の違いに備えるべきか

一般に、ここでの考慮事項はいずれの場合も同じになると思いますが、Azureでは、大きなインデックスによって課される過剰なメモリ/ IOコストがより直接的に見えるため、サービス層を微調整できるため、インフラストラクチャコストをより簡単に調整できますハードウェアリソースのセットが比較的固定されている。vcoreベースの価格設定の代わりに標準/プレミアム階層を使用する場合、プレミアムにはDTUあたりのIOが大幅に増えるため、標準のIOコストの影響が大きくなります。Azureでマルチリージョンバックアップ、冗長性、またはその他のローカル以外の機能を使用している場合は、不必要に広いインデックスが占める余分なスペースに関連する帯域幅コストが発生する可能性があります。


私たちは先に進み、この削除を行いました。副作用として、特定のテーブルでは、SELECT指定なしでORDER BY以前と同じ行が返されますが、任意の順序が異なります。
T2PS 2019

それは予想外ではありません。「ORDER BY」のない結果の順序は、定義により未定義であり、クエリプランナーが別のアプローチを取ることを決定するたびに変更される可能性があります。これは、インデックスの変更またはデータパターンの増加の結果として行われる可能性があります。他の要因により、この変更がなくても、後でこのような順序変更が行われる場合があります。ステートメントの出力の順序付けに表面的にも依存している場合は、それを保証するために「ORDER BY」を含める必要があります。
デビッドスピレット

ああ、間違いなく。以前のコメントは、後でこの回答を見つけた人のための注意書きとしてより多くのものを意味していました。
T2PS 2019

5

問題は、これは賢明な戦略ですか?...(この戦略が最適だったとしても、今ではデフォルトではないという疑問が提起されていますが)

ほとんどの場合、これは賢明な戦略ではありません。その理由は、一般的なOLTPデータベースでは、エンドユーザーに返される行はそれほど多くないためです。(汎化)

あなたが自問すべき問題は、キー列でシークしている場合、そのシーク操作によって返される行数は何ですか?そして、その列を検索するクエリについても同じことを繰り返します。

たくさんの列を返す次の表を検討してください。 where SelectiveIDField= ...

select columnA,columnC, ... columnZ
FROM dbo.BigTable
Where SelectiveIDField= '225122141';

seek onによって1行のみが返される場合selectiveIDField、追加のキー検索はそんなに悪いことですか? (ここにクラスター化インデックスがあると思いますが、そうでなければRIDルックアップです)

これは、1つの追加のキールックアップ、1つの追加の実行+結合演算子を実行するだけです。それが10であっても100であっても、それは大きな影響を与えますか?これは、クエリの実行量と実行時間の重要性にも依存します。

無視できる場合は、インデックスを作成しSelectiveIDFieldて1日で呼び出すだけです。書き込み損失と比較して、読み取り利益に見合う価値はありません。

要するに、テーブル全体にインデックスを作成することは、クエリに実際に問題があり、カバーするインデックス全体を追加することで大幅に改善できる場合を除いて、デフォルトのアプローチではないはずです。

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