クライアントから提供されたクエリのパフォーマンスを改善するための最良の方法を知りたいと思っています。結合されたいくつかのテーブルが含まれており、テーブルの1つはdwh.fac_sale_detail
と呼ばれ、15億行を含みます。
このテーブルdwh.fac_sale_detail
は、TradingDateKey1
という列の1つに基づいて分割されます。実際にはyyyymmdd形式でデータを保存しますが、INT
データ型です。
これには2005年から2015年までのTradingDateKeysがありますが、パーティションは2014年までのみ作成されます。
別のチームの1人が以下のことをアドバイスし、私は彼のアドバイスに従っていますが、パーティションの作成または変更は初めてであり、これがクエリのパフォーマンスに実際に影響を与えるかどうかはわかりません。
彼が自分の言葉で言ったことは、「_FactSalesDetail
テーブルは現在約15億行であり、現在TradingDate
で年間約10のパーティションにパーティション化されています。パーティションごとに1億5,000万行。最後の年を月ごとのパーティションにさらにパーティション化し、すべてのパーティションに列ストアインデックスを適用することをお勧めします。各パーティションにインデックスを適用することは1回限りであり、メンテナンスする必要があるだけです。今後の現在のパーティションのインデックス。 "
これが、最適化しようとしているクエリの クエリプラン です。
理解を深めるために、添付のスクリーンショットもご覧ください。
クエリプランを追加していただきありがとうございます。それは非常に有益です。クエリプランに基づいたいくつかの推奨事項がありますが、最初に注意点があります。私が言ったことを理解してそれが正しいと仮定するのではなく、まず試して(理想的にはテスト環境で)、変更が行われる理由を理解してください。またはクエリを改善しないでください!
クエリプラン:概要
このクエリプラン(および対応するXML)から、すぐにいくつかの有用な情報を確認できます。
最適化:列ストアかどうか
これは難しい質問ですが、この場合、コラムストアはお勧めしません。主な理由は、SQL 2012を使用しているためです。SQL2014にアップグレードできる場合は、列ストアを試してみる価値があると思います。
一般に、クエリは列ストアが設計されたタイプであり、列ストアのI/Oの削減とバッチモードのCPU効率の向上から大きな恩恵を受けることができます。
ただし、 SQL 2012の列ストアの制限 は大きすぎます。流出が発生するとSQL Serverがバッチモードを完全に破棄する tempdb流出動作 は、処理している大量の行で発生する可能性のある壊滅的なペナルティ。 SQL 2012で列ストアを使用する場合は、すべてのクエリを非常に密接にベビーシッターし、常にバッチモードを使用できるように準備してください。
最適化:パーティションを増やしますか?
これ以上のパーティションがこの特定のクエリに役立つとは思いません。もちろん、試してみても構いませんが、パーティショニングは主にデータ管理機能(SWITCH PARTITION
を介してETLプロセスで新しいデータをスワップインする機能であり、パフォーマンス機能ではないことを覚えておいてください。場合によっては、明らかにパフォーマンスの向上に役立ちます) 、しかし同様に他のものでパフォーマンスを損なう可能性があります(たとえば、パーティションごとに1回実行する必要がある多くのシングルトンシーク)。
Columnstoreを使用する場合、パーティション分割よりも データをロードしてセグメントを削除する の方が重要だと思います。理想的には、完全な列ストアセグメントと優れた圧縮率を実現するために、各パーティションにできるだけ多くの行が必要になるでしょう。
最適化:カーディナリティ推定の改善
巨大なファクトテーブルと各ディメンションテーブルからの非常に小さな(数百または数千行)の行セットがあるため、使用するディメンション行のみを含む一時テーブルを明示的に作成するアプローチをお勧めします。たとえば、cast(right(ALHDWH.dwh.Dim_Date.Financial_Year,4) as int) IN ( 2015, 2014, 2013, 2012, 2011 )
のような複雑なロジックを使用してDim_Date
に結合するのではなく、Dim_Date
から関心のある行のみを抽出して適切なPKをそれらの行に追加する前処理クエリを記述する必要があります。
これにより、SQL Serverは実際に使用している行のみの統計を作成できるようになり、計画全体でより優れたカーディナリティの見積もりが得られる可能性があります。この前処理は、全体的なクエリの複雑さに比べて非常に簡単な作業であるため、このオプションを強くお勧めします。
最適化:スレッドの歪みを減らす
Dim_Date
から独自のテーブルにデータを抽出し、そのテーブルに主キーを追加すると、スレッドスキュー(スレッド間の作業の不均衡)を減らすのにも役立つ可能性があります。これが理由を示すのに役立つ画像です:
この場合、Dim_Date
テーブルには22,000行あり、SQL Serverはこれらの行のうち7,700を使用する予定であり、実際に使用したのは1,827行のみでした。
SQL Serverは行の範囲をスレッドに割り当てるために統計を使用するため、この場合のカーディナリティの見積もりが低いことが、行の非常に不十分な分布の根本的な原因である可能性があります。
1,872行のスレッドスキューはそれほど重要ではないかもしれませんが、問題は15億行のファクトテーブルへのシークにカスケードされ、2つのスレッドによって6億行が処理されている間に30のスレッドがアイドル状態にあることです。
最適化:分類の流出を取り除く
私が焦点を当てるもう1つの分野は、分類流出です。この場合の主な問題は、カーディナリティの見積もりが悪いことだと思います。以下に示すように、SQL Serverは、Sort
とStream Aggregate
の組み合わせによって実行されるグループ化操作によって3億2400万行が生成されると考えています。ただし、実際には643,000行しか生成されません。
SQL Serverがこのグループから出てくる行がほとんどないことを知っている場合、HASH GROUP
句を実装するために、SORT GROUP
(Sort-Stream)ではなくGROUP BY
(ハッシュ集約)を使用することはほぼ確実です。
カーディナリティの見積もりを改善するために、上記の他の変更をいくつか加えた場合、これが自動的に修正される可能性があります。ただし、そうでない場合は、SQL Serverに強制するためにOPTION (HASH GROUP)
query hint を使用することを試みることができます。これにより、改善の規模を評価し、本番環境でクエリヒントを使用するかどうかを決定できます。私は一般的にクエリヒントに注意しますが、HASH GROUP
だけを指定することは、ジョインヒントを使用する、FORCE ORDER
を使用する、またはクエリオプティマイザーの手から制御を奪うなどの方法よりもはるかに軽いタッチです。
最適化:メモリ許可
最後の潜在的な問題の1つは、SQL Serverがクエリで72GBのメモリを使用することを想定していると推定しましたが、サーバーはクエリにこれだけのメモリをプロビジョニングできませんでした。サーバーにメモリを追加すると効果があることは技術的には真実ですが、この問題を攻撃する方法は少なくとも他にいくつかあると思います。
Sort
演算子を取り除きます。クエリでかなりのメモリ許可を消費する唯一の演算子ですSTARクエリの最適化は、多くの点で他のクエリスタイルの最適化と同じです。さらに、STARクエリにはいくつかの特別な考慮事項があります。基本と基本の一部を再訪することはあなたにお金を払うかもしれません。
PRECISION STYLEとWAREHOUSE STYLEの比較。 STARまたはその他のクエリはPRECISIONまたはWAREHOUSEスタイルとして分類できます。これは、多数の行または少数の行を返すことを意味します。ラージとスモールは、ほとんどの場合、フィルタリング後のテーブルからの%OF ROWSとして定義され、このコンテキスト内では、2%のルールがしばしば引用されます。このパーセンテージは、ほとんどのクエリ状況のガイドラインとして、実際の実践では数学的に健全で成功しているためです。クエリスタイルの定義つまり、テーブル内の行の2%未満が必要な場合は、それらの行をフェッチするためにインデックスが適している可能性がありますが、テーブル内の行の2%を超える場合は、テーブルスキャンの方が優れている可能性があります。はい、フリンジケースがあり、特定のデータスキューのバリエーションがあり、異なるデータベース管理製品には異なる機能があります。つまり、実際の%は2%よりも大きいまたは小さい可能性がありますが、全体的にこのルールは、特にクエリを最適化するための出発点としてうまく機能します。
「2%RULE%」という考えを踏まえると、真のSTARクエリは、ファクトテーブルの行の<2%を返すことを目的としています。ディメンションで囲まれ、BITMAPインデックスでカバーされるファクトテーブルは、この設計に対するクエリがSTARクエリであることを意味しません。ファクトテーブルから行の2%以上を返す「STARクエリ」は、低速のSTARクエリではなく、誤ったデータベース設計を使用したデータマイニング操作です。さらに、BITMAPインデックスは、最適化のSTARクエリ問題のために特別に作成されました。したがって、STARクエリの場合、特定の単一インデックスで行の2%未満を特定する必要はありませんが、検索されたすべてのインデックスがBITMAP MERGEステップで結合された後、返される結果の行セットはファクトテーブルの行の2%未満である必要があります。これは、集計された行ではなく、ファクトテーブルのRAW行で定義されます。それ以外の場合は、ほとんどの場合FACTテーブルをスキャンするだけで、インデックスの時間をまったく無駄にしない方がよいでしょう。したがって、STAR Queryでさえ、最適化空間ではPRECISION対WAREHOUSEスタイルのコンセプトが支配的です。
パフォーマンスのためのパーティショニング。パフォーマンスの観点からのパーティション分割には、2つの主要なユーティリティがあります。最初はパーティションのプルーニングです。述語がパーティションキーに対するフィルタリングを提供するクエリの場合、オプティマイザはパーティションをスキャンするのではなく、パーティション全体をスキップできます。これにより、IOが減少します。 2番目の用途は、パーティションペアとして実行される並列結合を有効にすることです。クエリ内の2つのテーブルが両方ともクエリの結合キー全体で等分割されている場合、そのクエリは、一致するパーティションのペアのみを使用して2つのテーブルを並列に結合できます。これにより、結合が非常にスケーラブルになり、必要なメモリが大幅に削減されます。繰り返しになりますが、ここで重要なのは、結合列全体の等分割です。
円柱状のストレージとパフォーマンス。円柱データストアは、2つの基本的な方法でもパフォーマンスを提供します。最初は圧縮です。このストレージ戦略は、同様のデータを配置します。したがって、特に事前ソートのステップが実行される場合、列データストアで大きな圧縮率を達成できます。この圧縮により、適切な条件下でIOコストが大幅に削減されます。2番目に、このストレージステートジーは、ほとんどのクエリがすべての列を必要としないという事実を利用しようとします。したがって、列データストアはスキップの可能性を開きますクエリで必要とされない列。これでもIOコストを削減できます。一般的に、クエリでテーブルの列の5%未満が必要な場合、列からのパフォーマンスが向上します予測は重要になる場合があります。
事前集計。サマリー表は、依然としてパフォーマンスに重要な役割を果たしています。詳細を取得して必要になるたびに合計するよりも、すでに合計された行をフェッチする方が常に速くなります。 STARクエリスペースでは、これは通常、クエリを監視して、スライス/ダイシング/階層トラバースされている最も一般的なディメンションを確認することを意味します。データベースシステムが必要に応じてこれらのサマリーオブジェクトを透過的に識別できると仮定すると、これらはサマリーテーブルの良い候補になります。
これを念頭に置いて、いくつかの質問をすることから始めます。
あなたは正しいデザインを持っていますか?あなたのクエリは本当に、最終的にファクトテーブルの行の2%未満を再調整するSTARクエリですか?または、間違ったストレージ設計に対して大きなことをしようとしていますか?
クエリは、パーティションスキームにマップしますか?もしそうなら、彼らはパーティショニング(実際にはプルーニングを行っています)を利用していますか?もしそうなら、あなたはおそらく結合の1つにわたる並列処理の候補ですか?
何%の列を探していますか?圧縮を使用してスペース要件を大幅に削減できるため、IOを削減してクエリ時間を短縮できますか?テーブルで5%未満の列を探しているので、列ストアがテーブルの不要な部分をスキップすることでメリットを得られる可能性があります。
IOからのクエリコストと、結合/集計/並べ替えからのコストは?コストがどこにあるかによって、パフォーマンス機能の選択が決まります。
存在する場合にクエリのパフォーマンスプロファイルを劇的に変化させる特定の集計はありますか?
友達のアドバイスについては、さらに説明を求める必要があります。そこには明らかに多くの仮定があります。 1.古いパーティションは読み取り専用であること。 2.クエリが年をまたがるのではなく特定の月をフェッチしていること(たとえば、2つの四半期を比較します)。
最後に、友人のパーティショニングと列ストアが大きな助けになる可能性がある場合、それはクエリが実際にはSTARクエリではないことを示唆していることを考慮してください。全体的なパフォーマンスにはほとんど意味がありません。したがって、最初にクエリが必要とするものを確認し、それを適切なストレージ設計にマップする必要があることは明らかです。