インデックスが実行を高速化せず、場合によってはクエリが遅くなります。なぜそうですか?


34

速度を上げるためにインデックスを試していましたが、結合の場合、インデックスはクエリの実行時間を改善せず、場合によっては速度が低下します。

テストテーブルを作成してデータを入力するクエリは次のとおりです。

CREATE TABLE [dbo].[IndexTestTable](
    [id] [int] IDENTITY(1,1) PRIMARY KEY,
    [Name] [nvarchar](20) NULL,
    [val1] [bigint] NULL,
    [val2] [bigint] NULL)

DECLARE @counter INT;
SET @counter = 1;

WHILE @counter < 500000
BEGIN
    INSERT INTO IndexTestTable
      (
        -- id -- this column value is auto-generated
        NAME,
        val1,
        val2
      )
    VALUES
      (
        'Name' + CAST((@counter % 100) AS NVARCHAR),
        RAND() * 10000,
        RAND() * 20000
      );

    SET @counter = @counter + 1;
END

-- Index in question
CREATE NONCLUSTERED INDEX [IndexA] ON [dbo].[IndexTestTable]
(
    [Name] ASC
)
INCLUDE (   [id],
    [val1],
    [val2])

クエリ1は改善されています(わずかですが、改善は一貫しています):

SELECT *
FROM   IndexTestTable I1
       JOIN IndexTestTable I2
            ON  I1.ID = I2.ID
WHERE  I1.Name = 'Name1'

インデックスなしの統計と実行プラン(この場合、テーブルはデフォルトのクラスター化インデックスを使用します):

(5000 row(s) affected)
Table 'IndexTestTable'. Scan count 2, logical reads 5580, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 109 ms,  elapsed time = 294 ms.

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

インデックスが有効になりました:

(5000 row(s) affected)
Table 'IndexTestTable'. Scan count 2, logical reads 2819, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 94 ms,  elapsed time = 231 ms.

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

これで、インデックスが原因でスローダウンするクエリ(クエリはテスト専用に作成されているため、意味がありません):

SELECT I1.Name,
       SUM(I1.val1),
       SUM(I1.val2),
       MIN(I2.Name),
       SUM(I2.val1),
       SUM(I2.val2)
FROM   IndexTestTable I1
       JOIN IndexTestTable I2
            ON  I1.Name = I2.Name
WHERE   
       I2.Name = 'Name1'
GROUP BY
       I1.Name

クラスタ化インデックスを有効にした場合:

(1 row(s) affected)
Table 'IndexTestTable'. Scan count 4, logical reads 60, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 1, logical reads 155106, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 17207 ms,  elapsed time = 17337 ms.

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

インデックスが無効になりました:

(1 row(s) affected)
Table 'IndexTestTable'. Scan count 5, logical reads 8642, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 2, logical reads 165212, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 17691 ms,  elapsed time = 9073 ms.

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

質問は次のとおりです。

  1. インデックスはSQL Serverによって提案されていますが、大きな違いによって速度が低下するのはなぜですか?
  2. ほとんどの時間を費やしているネストされたループ結合とその実行時間を改善する方法は何ですか?
  3. 私が間違っていることや見逃していることはありますか?
  4. デフォルトのインデックス(主キーのみ)で時間がかかるのはなぜですか。また、非クラスター化インデックスが存在する場合、結合テーブルの各行について、結合はName列にあるため、結合テーブルの行をすばやく見つける必要があります。インデックスが作成されました。これはクエリ実行計画に反映され、IndexAがアクティブな場合はIndex Seekコストは低くなりますが、それでも遅いのはなぜですか?また、スローダウンの原因となっているネストされたループの左外部結合には何がありますか?

SQL Server 2012を使用する

回答:


23

インデックスはSQL Serverによって提案されていますが、大きな違いによって速度が低下するのはなぜですか?

インデックスの提案は、クエリオプティマイザーによって行われます。既存のインデックスでは十分に機能しないテーブルからの論理的な選択に出くわした場合、「インデックスが欠落している」という提案を出力に追加する場合があります。これらの提案は日和見的です。それらはクエリの完全な分析に基づいていないため、より広い考慮事項を考慮していません。せいぜい、それらはより有用なインデックス付けが可能になる可能性があることを示すものであり、熟練したDBAが検討する必要があります。

不足しているインデックスの提案について他に言うことは、それらがオプティマイザのコストモデルに基づいており、オプティマイザは、提案されたインデックスがクエリの推定コストをどれだけ削減できるかによって推定します。ここでのキーワードは「モデル」と「推定値」です。クエリオプティマイザーは、ハードウェア構成やその他のシステム構成オプションについてほとんど知りません。そのモデルは、ほとんどの場合、ほとんどのシステムでほとんどの人にとって妥当な計画結果を生成する固定数値に基づいています。使用される正確なコスト数の問題は別として、結果は常に推定値であり、推定値は間違っている可能性があります。

ほとんどの時間を費やしているネストされたループ結合とその実行時間を改善する方法は何ですか?

クロス結合操作自体のパフォーマンスを改善するために行うことはほとんどありません。ネストされたループは、クロス結合で可能な唯一の物理的な実装です。結合の内側のテーブルスプールは、各外側の行の内側を再スキャンしないようにする最適化です。これが有用なパフォーマンスの最適化であるかどうかはさまざまな要因に依存しますが、私のテストでは、クエリを使用しない方が良いでしょう。繰り返しますが、これはコストモデルを使用した結果です-私のCPUおよびメモリシステムは、おそらくあなたとは異なるパフォーマンス特性を持っています。テーブルスプールを回避するための特定のクエリヒントはありませんが、スプールがある場合とない場合の実行パフォーマンスをテストするために使用できる文書化されていないトレースフラグ(8690)があります。これが実際の運用システムの問題である場合、TF 8690を有効にして作成されたプランに基づいたプランガイドを使用して、スプールのないプランを強制することができます。本番環境で文書化されていないトレースフラグを使用することは、インストールが技術的にサポートされなくなり、トレースフラグが望ましくない副作用を引き起こす可能性があるため、お勧めしません。

私が間違っていることや見逃していることはありますか?

欠落している主なものは、非クラスター化インデックスを使用するプランのオプティマイザーのモデルによると、推定コストは低くなりますが、実行時の問題が大きくなることです。Clustered Indexを使用して、プラン内のスレッド間で行の分布を見ると、おそらくかなり良い分布が見られます。

スキャン計画

Nonclustered Index Seekを使用する計画では、作業は完全に1つのスレッドによって実行されます。

プランを探す

これは、並列スキャン/シーク操作によってスレッド間で作業が分散される方法の結果です。パラレルスキャンがインデックスシークよりも作業を分散することが常にあるとは限りませんが、この場合はそうなります。より複雑な計画には、スレッドをまたいで作業を再配分するための再パーティション化交換が含まれる場合があります。この計画にはそのような交換がないため、行がスレッドに割り当てられると、関連するすべての作業が同じスレッドで実行されます。実行計画内の他の演算子の作業配分を見ると、すべての作業がインデックスシークの場合と同じスレッドによって実行されていることがわかります。

スレッド間の行分散に影響を与えるクエリヒントはありません。重要なことは、その可能性を認識し、実行プランの十分な詳細を読み取って問題の原因を判断できるようにすることです。

デフォルトのインデックス(主キーのみ)で時間がかかるのはなぜですか。また、非クラスター化インデックスが存在する場合、結合テーブルの各行について、結合はName列にあるため、結合テーブルの行をすばやく見つける必要があります。インデックスが作成されました。これはクエリ実行計画に反映され、IndexAがアクティブな場合はIndex Seekコストは低くなりますが、それでも遅いのはなぜですか?また、スローダウンの原因となっているネストされたループの左外部結合には何がありますか?

予想どおり、非クラスター化インデックスプランの方が潜在的に効率的であることは明らかです。パフォーマンスの問題の原因となるのは、実行時のスレッド間での作業の不十分な分散です。

例を完了し、私が言及したことのいくつかを説明するために、より良い作業分散を得るための1つの方法は、一時テーブルを使用して並列実行を駆動することです。

SELECT
    val1,
    val2
INTO #Temp
FROM dbo.IndexTestTable AS ITT
WHERE Name = N'Name1';

SELECT 
    N'Name1',
    SUM(T.val1),
    SUM(T.val2),
    MIN(I2.Name),
    SUM(I2.val1),
    SUM(I2.val2)
FROM   #Temp AS T
CROSS JOIN IndexTestTable I2
WHERE
    I2.Name = 'Name1'
OPTION (FORCE ORDER, QUERYTRACEON 8690);

DROP TABLE #Temp;

これにより、より効率的なインデックスシークを使用し、テーブルスプールを使用せず、スレッド間で作業を適切に分散する計画が作成されます。

最適な計画

私のシステムでは、この計画はクラスター化インデックススキャンバージョンよりも大幅に高速に実行されます。

並列クエリ実行の内部について詳しく知りたい場合は、PASS Summit 2013セッションの記録をご覧ください。


0

それは実際にはインデックスの問題ではなく、もっとひどく記述されたクエリです。nameの一意の値は100のみであるため、名前ごとに5000の一意のカウントが残ります。

したがって、表1の各行に対して、表2から5000を結合しています。25020004行と言えますか。

これを試してください。これはリストしたインデックスが1つしかないことに注意してください。

    DECLARE @Distincts INT
    SET @Distincts = (SELECT  TOP 1 COUNT(*) FROM IndexTestTable I1 WHERE I1.Name = 'Name1' GROUP BY I1.Name)
    SELECT I1.Name
    , @Distincts
    , SUM(I1.val1) * @Distincts
    , SUM(I1.val2) * @Distincts
    , MIN(I2.Name)
    , SUM(I2.val1)
    , SUM(I2.val2)
    FROM   IndexTestTable I1
    LEFT OUTER JOIN

    (
        SELECT I2.Name
        , SUM(I2.val1) val1
        , SUM(I2.val2) val2
        FROM IndexTestTable I2
        GROUP BY I2.Name
    ) I2 ON  I1.Name = I2.Name
    WHERE I1.Name = 'Name1'
    GROUP BY  I1.Name

そして時間:

    SQL Server parse and compile time: 
       CPU time = 0 ms, elapsed time = 8 ms.
    Table 'IndexTestTable'. Scan count 1, logical reads 31, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

     SQL Server Execution Times:
       CPU time = 0 ms,  elapsed time = 1 ms.

    (1 row(s) affected)
    Table 'IndexTestTable'. Scan count 2, logical reads 62, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

     SQL Server Execution Times:
       CPU time = 16 ms,  elapsed time = 10 ms.

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

不正な形式のクエリのSQLインデックスを非難することはできません


1
答えてくれて、はい、クエリは改善できますが、私の質問のロジックは、デフォルトのインデックス(プライマリキーのみ)で、なぜ時間がかからず、非クラスタ化インデックスが存在するのかということです。結合テーブル、結合テーブルの行はより速く検出される必要があります。これはクエリ実行プランに反映され、IndexAがアクティブな場合はIndex Seekのコストは低くなりますが、それでも遅いのはなぜですか?また、スローダウンの原因となっているネストされたループの左外部結合には何がありますか?質問をより明確にするために、質問を編集してこのコメントを追加しました。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.