このクエリで非クラスター化インデックスが使用されないのはなぜですか?


12

クエリパフォーマンスの向上に関するこの質問に続き、デフォルトでインデックスを使用する方法があるかどうかを知りたいと思います。

このクエリは約2.5秒で実行されます。

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31';

これは約33msで実行されます。

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31' 
ORDER BY [DateEntered], [DeviceID];

[ID]フィールド(pk)にクラスター化インデックスがあり、[DateEntered]、[DeviceID]に非クラスター化インデックスがあります。最初のクエリはクラスター化インデックスを使用し、2番目のクエリは非クラスター化インデックスを使用します。私の質問は2つの部分です。

  • なぜ、両方のクエリに[DateEntered]フィールドにWHERE句があるため、サーバーは2番目ではなく最初のクラスター化インデックスを使用するのですか?
  • orderbyがなくても、このクエリでデフォルトで非クラスタ化インデックスを使用するにはどうすればよいですか?(または、なぜそのような振る舞いを望まないのでしょうか?)

DateEnteredはDateTimeであり、この場合は日付部分を使用していますが、日付と時刻の両方に対してクエリを実行することもあります。
ネイト

回答:


9

最初のクエリは、前に説明したしきい値に基づいてテーブルスキャンを実行します。数百万行の狭いテーブルでクエリのパフォーマンスを向上させることはできますか?

(ほとんどのTOP 1000場合、句のないクエリは46k行以上を返します。または35k〜46kの間の一部を返します。(灰色の領域;-))

2番目のクエリは、順序付けする必要があります。NCインデックスは必要な順序で並べられているため、オプティマイザーがそのインデックスを使用し、クラスター化インデックスをブックマーク検索して、クラスター化インデックススキャンの実行と比較して不足している列を取得する方が安価です。それを注文する。

ORDER BY句の列の順序を逆にすると、NC INDEXが役に立たなくなるため、クラスター化インデックススキャンに戻ります。

編集はあなたの2番目の質問への答えを忘れました、なぜあなたはこれを望まないのですか

非クラスター化非カバーインデックスを使用すると、NCインデックスでrowIDが検索され、クラスター化インデックスで不足している列を検索する必要があります(クラスター化インデックスにはテーブルのすべての列が含まれます)。クラスター化インデックスで不足している列をルックアップするIOは、ランダムIOです。

ここで重要なのはランダムです。NCインデックスで見つかったすべての行について、アクセス方法はクラスター化インデックスで新しいページを検索する必要があるためです。これはランダムであるため、非常に高価です。

一方で、オプティマイザーはクラスター化インデックススキャンを実行することもできます。割り当てマップを使用してスキャン範囲をルックアップし、大きな塊でクラスター化インデックスの読み取りを開始できます。これはシーケンシャルで、はるかに安価です。(テーブルが断片化されていない限り:-))欠点は、完全なクラスター化インデックスを読み取る必要があることです。これはバッファにとっては悪いことであり、潜在的に膨大な量のIOです。それでも、シーケンシャルIOです。

あなたの場合、オプティマイザーは35kから46kの間の行を決定します。完全なクラスター化インデックススキャンの方が安価です。ええ、それは間違っています。また、多くの場合、選択的WHERE句や大きなテーブルを使用しない狭い非クラスタ化インデックスでは、これはうまくいきません。(非常に狭いテーブルでもあるため、あなたのテーブルはさらに悪くなります。)

現在、を追加ORDER BYすると、完全なクラスター化インデックスをスキャンして結果を並べ替えるのに費用がかかります。代わりに、オプティマイザーは、注文済みのNCインデックスを使用して、ブックマークの検索にランダムなIOペナルティを支払う方が安価であると想定します。

したがって、ご注文は完璧な「クエリヒント」のようなソリューションです。しかし、ある時点で、クエリ結果が非常に大きくなると、ブックマークルックアップランダムIOのペナルティは非常に大きくなり、遅くなります。オプティマイザーはその時点の前にプランをクラスター化インデックススキャンに戻すと思いますが、確実にはわかりません。

あなたの場合、チャットと前の質問(リンクを参照)で説明したように、挿入がenterddateで順序付けられている限り、enteredDate列にクラスター化インデックスを作成する方が適切です。


20

異なる構文を使用してクエリを表現すると、非クラスター化インデックスを使用するという希望をオプティマイザーに伝えるのに役立つ場合があります。以下のフォームを使用すると、必要な計画を確認できます。

SELECT
    [ID],
    [DeviceID],
    [IsPUp],
    [IsWebUp],
    [IsPingUp],
    [DateEntered]
FROM [dbo].[Heartbeats]
WHERE
    [ID] IN
(
    -- Keys
    SELECT TOP (1000)
        [ID]
    FROM [dbo].[Heartbeats]
    WHERE 
        [DateEntered] >= CONVERT(datetime, '2011-08-30', 121)
        AND [DateEntered]  < CONVERT(datetime, '2011-08-31', 121)
);

クエリプラン

そのプランを、非クラスター化インデックスがヒントで強制されたときに作成されたプランと比較します。

SELECT TOP (1000) 
    * 
FROM [dbo].[Heartbeats] WITH (INDEX(CommonQueryIndex))
WHERE 
    [DateEntered] BETWEEN '2011-08-30' and '2011-08-31';

強制インデックスヒントプラン

プランは基本的に同じです(キールックアップはクラスター化インデックスのシークにすぎません)。両方の計画フォームは、非クラスター化インデックスで1回のシークと、クラスター化インデックスへの最大1000回のルックアップのみを実行します。

重要な違いは、トップ演算子の位置です。2つのシークの間に位置するTopは、オプティマイザーが2つのシーク操作をクラスター化インデックスの論理的に等価なスキャンに置き換えることを防ぎます。オプティマイザーは、論理プランの一部を同等のリレーショナル操作で置き換えることにより機能します。topは関係演算子ではないため、書き換えによってクラスター化インデックススキャンへの変換が妨げられます。オプティマイザーがTop演算子の位置を変更できた場合でも、コストの見積もりの​​仕組みにより、シーク+ルックアップよりもスキャンが優先されます。

スキャンとシークのコスト

非常に高いレベルでは、スキャンとシークのオプティマイザーのコストモデルは非常に単純です。320回のランダムシークのコストは、スキャンで1350ページを読み取るのと同じであると推定されます。これはおそらく、特定の最新のI / Oシステムのハードウェア機能とはほとんど似ていませんが、実用的なモデルとしては十分に機能します。

また、このモデルでは、すべてのクエリがデータまたはインデックスページが既にキャッシュにない状態で開始されると想定されているという主要なものとして、いくつかの単純化された仮定を行っています。含意は、すべてのI / Oが物理的なI / Oになるということです-ただし、実際にはそうなることはめったにありません。コールドキャッシュであっても、プリフェッチと先読みは、クエリプロセッサが必要とするまでに、必要なページが実際にメモリ内にある可能性が高いことを意味します。

もう1つの考慮事項は、メモリにない行に対する最初の要求により、ページ全体がディスクからフェッチされることです。同じページの行に対する後続のリクエストでは、物理I / Oは発生しません。原価計算モデルには、このような効果を考慮するためのロジックが含まれていますが、完全ではありません。

これらすべての(およびそれ以上の)ことは、オプティマイザーが通常よりも早くスキャンに切り替える傾向があることを意味します。ランダムなI / Oは、物理的な操作が行われる場合にのみ、「シーケンシャル」I / Oよりも「はるかに高価」です。メモリ内のページへのアクセスは実際に非常に高速です。物理的な読み取りが必要な場合でも、断片化のためにスキャンによってシーケンシャル読み取りがまったく行われない場合があり、パターンが本質的にシーケンシャルになるようにシークが連結される場合があります。それに加えて、最新のI / Oシステム(特にソリッドステート)のパフォーマンス特性の変化と全体が非常に不安定に見え始めます。

行の目標

計画内にトップオペレーターが存在すると、原価計算アプローチが変更されます。オプティマイザーは、スキャンを使用して1000行を検索する場合、クラスター化インデックス全体をスキャンする必要がない可能性があることを認識できるほど賢く、1000行が見つかったらすぐに停止できます。Top演算子で1000行の「行の目標」を設定し、統計情報を使用してそこから戻り、行ソース(この場合はスキャン)から必要な行数を推定します。ここでこの計算の詳細について書きました

この回答の画像は、SQL Sentry Plan Explorerを使用して作成されました。

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