オプティマイザが非クラスタ化インデックスの代わりにクラスタ化インデックス+ソートを選択するのはなぜですか?


11

次の例を考えてみましょう:

IF OBJECT_ID('dbo.my_table') IS NOT NULL
    DROP TABLE [dbo].[my_table];
GO

CREATE TABLE [dbo].[my_table]
(
    [id]    int IDENTITY (1,1)  NOT NULL PRIMARY KEY,
    [foo]   int                 NULL,
    [bar]   int                 NULL,
    [nki]   int                 NOT NULL
);
GO

/* Insert some random data */
INSERT INTO [dbo].[my_table] (foo, bar, nki)
SELECT TOP (100000)
    ABS(CHECKSUM(NewId())) % 14,
    ABS(CHECKSUM(NewId())) % 20,
    n = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
FROM 
    sys.all_objects AS s1 
CROSS JOIN 
    sys.all_objects AS s2
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_my_table]
    ON [dbo].[my_table] ([nki] ASC);
GO

[nki](非クラスター化インデックス)で並べ替えられたすべてのレコードをフェッチする場合:

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table ORDER BY nki;
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 266 ms, elapsed time = 493 ms

オプティマイザーはクラスター化インデックスを選択し、並べ替えアルゴリズムを適用します。

ここに画像の説明を入力してください

Execution plan

しかし、非クラスター化インデックスを使用するように強制した場合:

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table WITH(INDEX(IX_my_TABLE));
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 311 ms, elapsed time = 188 ms

次に、キールックアップで非クラスター化インデックスを使用します。

ここに画像の説明を入力してください

Execution plan

明らかに、非クラスター化インデックスがカバーするインデックスに変換される場合:

CREATE UNIQUE NONCLUSTERED INDEX [IX_my_table]
    ON [dbo].[my_table] ([nki] ASC)
    INCLUDE (id, foo, bar);
GO

次に、このインデックスのみを使用します。

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table ORDER BY nki;
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 32 ms, elapsed time = 106 ms

ここに画像の説明を入力してください

Execution plan


質問

  • 後者の場合、実行時間が38%速くても、SQL Serverは非クラスター化インデックスを使用する代わりにクラスター化インデックスと並べ替えアルゴリズムを使用するのはなぜですか?

1
強制インデックスクエリでORDER BYを省略することを意味しましたか?
フォレスト

回答:


9

後者の場合、実行時間が38%速くても、SQL Serverは非クラスター化インデックスを使用する代わりにクラスター化インデックスと並べ替えアルゴリズムを使用するのはなぜですか?

SQL Serverは、ランタイム情報ではなく、統計に基づいたコストベースのオプティマイザーを使用するためです。

このクエリのコスト見積もりプロセスでは、実際にルックアッププランを評価しますが、より多くの労力がかかると見積もります。(実行プランでSELECTにカーソルを合わせると、「推定サブツリーコスト」に注意してください)。これも必ずしも悪い仮定ではありません。私のテストマシンでは、ルックアッププランは並べ替え/スキャンのCPUの6倍を占めています。

SQL Serverがルックアッププランのコストを高くする理由について、Rob Farleyの回答を参照してください。


9

100,000回のルックアップに必要な読み取りの数と並べ替えの実行に必要な読み取りの数を比較すると、クエリオプティマイザーがCIX + Sortが最良の選択であると計算する理由がすぐにわかります。

読み込まれているページがメモリにあるため、ルックアップの実行はより速くなります(キャッシュをクリアしても、ページごとに多くの行があり、同じページを何度も読み込んでいますが、断片化の量が異なりますまたは他のアクティビティとは異なるメモリプレッシャー、これはそうではないかもしれません)。CIX + Sortを高速化するのにそれほど多くの時間はかかりませんが、読み取りのコストは同じページを繰り返しヒットすることの相対的な安さを考慮していないためです。


4

私はこの質問を少し掘り下げることにしました。非クラスター化インデックスを(強制的に)使用するのではなく、どのように、いつ使用するか、またはおそらくより良い方法について話している興味深いドキュメントを見つけました。

John Eisbrenerのコメントで示唆されているように、他のブログでも最も参照されているものの1つが、Kimberly L. Trippの興味深い記事です。

ただし、これだけではありません。興味がある場合は、このページをご覧ください。

ご覧のように、それらはすべてTipping pointの概念を動き回っています。

KL Trippの記事からの引用

転換点は何ですか?

これは、返される行数が「十分に選択的でなくなった」ポイントです。SQL Serverは、対応するデータ行を検索するために非クラスター化インデックスを使用しないことを選択し、代わりにテーブルスキャンを実行します。

SQL Serverがヒープで非クラスター化インデックスを使用する場合、基本的には、ベーステーブルのページへのポインターのリストを取得します。次に、これらのポインターを使用して、行IDルックアップ(RID)と呼ばれる一連の操作で行を取得します。これは、少なくとも、返された行の数と同じ数のページ読み取りを使用することを意味します。プロセスはクラスタ化インデックスをベーステーブルとしていくらか似ていますが、同じ結果、より多くの読み取りを行います。

しかし、その転換点がいつ起こるのでしょうか?

もちろん、この人生のほとんどのことと同様に、それは次のように異なります...

深刻ではありませんが、ページあたりの行数に応じて、テーブル内のページ数の25%から33%の間で発生します。ただし、考慮すべき要素が他にもあります。

ITPRoTodayの記事からの引用

転換点に影響を与えるその他の要因RIDルックアップのコストは転換点に影響を与える最も重要な要因ですが、他にもいくつかの要因があります。

  • クラスタ化インデックスをスキャンする場合、物理I / Oははるかに効率的です。クラスター化インデックスデータは、インデックス順にディスクに順番に配置されます。その結果、ディスク上で横方向のヘッド移動がほとんどなくなり、I / Oパフォーマンスが向上します。
  • データベースエンジンがクラスター化インデックスをスキャンしているとき、ディスクトラックの次の数ページに必要なデータが含まれている可能性が高いことがわかります。そのため、通常の8KBページではなく、64KBチャンクで先読みを開始します。これにより、I / Oも高速になります。

ここで、統計IOを使用してクエリを再度実行すると、次のようになります。

SET STATISTICS IO ON;
SELECT id, foo, bar, nki FROM my_table WHERE nki < 20000 ORDER BY nki ;
SET STATISTICS IO OFF;

Logical reads: 312

SET STATISTICS IO ON;
SELECT id, foo, bar, nki FROM my_table WITH(INDEX(IX_my_TABLE));
SET STATISTICS IO OFF;

Logical reads: 41293

2番目のクエリには、最初のクエリよりも論理的な読み取りが必要です。

非クラスター化インデックスは避けるべきですか?

いいえ、クラスター化インデックスは役に立ちますが、時間をかけて、それを使って何を達成しようとしているのかを分析する追加の努力をする価値があります。

KL Trippの記事からの引用

それで、あなたは何をすべきですか?場合によります。データを熟知していて、大規模なテストを行う場合は、ヒントを使用することを検討してください(spsでプログラムで実行できる巧妙なことがいくつかあるので、すぐに投稿します)。しかし、(もし可能なら)はるかに良い選択は、カバーすることを検討することです(それが本当に私の主なポイントです:)。私のクエリではすべての列が必要なため(悪意のあるSELECT *)、カバリングは非現実的ですが、クエリがより狭く、優先度が高い場合、ヒントよりもカバリングインデックス(多くの場合)の方が適しています。クエリをカバーするインデックスで、ヒントはありません。

それが今のところパズルに対する答えですが、さらに深く学ぶべきことがたくさんあります。転換点は非常に良いことであり、通常はうまく機能します。ただし、インデックスを強制してパフォーマンスを向上できることがわかった場合は、調査を行い、それがこれであるかどうかを確認することをお勧めします。次に、ヒントがどれほど役立つ可能性があるかを考えます。これで、どこに集中できるかがわかります。

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