集計にインデックス付きビューを使用する-あまりにも良いですか?


28

かなり大きなレコード数(1000万から2000万行)のデータウェアハウスがあり、特定の日付の間にレコードを数えるクエリや、特定のフラグを持つレコードを数えるクエリを実行することがよくあります。

SELECT
    f.IsFoo,
    COUNT(*) AS WidgetCount
FROM Widgets AS w
JOIN Flags AS f
    ON f.FlagId = w.FlagId
WHERE w.Date >= @startDate
GROUP BY f.IsFoo

パフォーマンスはそれほど悪くありませんが、比較的遅くなる可能性があります(コールドキャッシュで10秒程度)。

最近、私GROUP BYはインデックス付きビューで使用できることを発見し、次のようなものを試しました

CREATE VIEW TestView
WITH SCHEMABINDING
AS
    SELECT
        Date,
        FlagId,
        COUNT_BIG(*) AS WidgetCount
    FROM Widgets
    GROUP BY Date, FlagId;
GO

CREATE UNIQUE CLUSTERED INDEX PK_TestView ON TestView
(
    Date,
    FlagId
);

その結果、最初のクエリのパフォーマンスは100ミリ秒未満になり、結果のビューとインデックスは100k未満になりました(行数が多いにもかかわらず、日付とフラグIDの範囲は、このビューに1000〜2000行しか含まれないことを意味します)。

おそらくこれはWidgetテーブルへの書き込みのパフォーマンスを低下させると思いましたが、いいえ-このテーブルへの挿入と更新のパフォーマンスは、私が知る限りほとんど影響を受けません(さらに、このテーブルは頻繁に更新されないデータウェアハウスであるためとにかく)

私には、これはあまりにも良いように思えます-それは本当ですか?この方法でインデックス付きビューを使用する場合、何に注意する必要がありますか?


2
実際に有効なSQLになるようにスクリプトを書き直せますか?あなたのスクリプトだと思うので、あなたSELECTCREATE VIEWスクリプトは間違っていCREATE INDEXます。
マークシンキンソン14

2
@MarkSinkinson謝罪は、架空のテーブルのための有効なSQLを記述しようとしていることは困難であることが判明
ジャスティン・

私にとって「あまりにも良い」という部分は、MAX、自己結合または外部結合を含むビュー、またはそれ自体が別のビューを参照するビューのインデックス付けなど、より高度なビューが必要なときに発生しました-SQL Server docs.microsoft.com/en-us/sql/relational-databases/views/…を許可しました。ですから、私はいつも過度に野心的になり、物事を縮小しなければなりません。しかし、より単純な集約のために、それらは本当に素晴らしいです-SUMさえサポートされます。
Simon_Weaver

回答:


29

既に述べたように、ビュー自体は少数の行のみを実体化するため、テーブル全体を更新しても、ビューの更新に伴う追加の I / Oは無視できます。おそらく、ビューを作成したときに感じる最大の痛みをすでに感じているでしょう。次に最も近いのは、ビュー内に新しい行を必要とする新しいIDの束とともに、数十億行をベーステーブルに追加する場合です。

これは本当であるには余りにもよくありません。インデックス付きビューを使用する方法を正確に使用している-または少なくとも最も効果的な方法の1つ:書き込み時に将来のクエリ集計に料金を支払う。これは、結果がソースよりもはるかに小さい場合、およびもちろん、基礎となるデータが更新されるよりも頻繁に集約が要求される場合(一般にOLTPよりもDWで一般的)に最適です。

残念なことに、多くの人はビューのインデックス付けは魔法だと考えています。インデックスはすべてのビュー、特にテーブルを単純に結合したりソースと同じ行数を生成する(あるいは乗算する)ビューをより効率的にしません。これらの場合、ビューからのI / Oは元のクエリと同じかそれよりも悪くなります。これは、行が同じかそれ以上であるだけでなく、多くの場合、より多くの列を格納および具体化するためです。したがって、それらを事前に具体化しても、SSDを使用しても、I / O、ネットワーク、およびクライアントの処理/レンダリングが依然として大きな結果セットをクライアントに返す際の主なボトルネックであるため、何の利点もありません。実行時の結合を回避することで得られる節約は、まだ使用している他のすべてのリソースと比較して測定できません。

非クラスター化インデックスと同様に、やりすぎないように注意してください。1つのテーブルに10個の異なるインデックス付きビューを追加すると、特にグループ化列がクラスタリングキーではない場合、ワークロードの書き込み部分により大きな影響が出ます。

まあ、私はこのトピックについてブログを書くつもりでした。


19

アーロンの回答はこの質問をよくカバーしていました。追加する2つのこと:

  1. 集計インデックス付きビューは、クロスローの競合とデッドロックを引き起こす可能性があります。通常、2つの挿入はデッドロックしません(ロックエスカレーションやロックハッシュ衝突などのかなりまれな条件を除く)。しかし、両方の挿入がビュー内の同じグループをアドレス指定すると、競合します。同じポイントは、ロック(DML、ロックヒント)を取得する他のものを表します。
  2. 集計されないインデックス付きビューも有用です。複数のテーブルの列にインデックスを付けることができます。これにより、1つのテーブルで効率的にフィルター処理し、結合されたテーブルの列で順序付けできます。そのパターンは、フルテーブル結合を小さな一定時間のクエリに変換できます。

集計ビューと結合ビューの両方を使用して、非常にメリットがありました。

すべてのユースケースはすべて、完璧なケースのようです。インデックス付きビューは、あまり活用されていない手法です。

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