4つのクラスター化列ストアインデックステーブル(CCI)と9つの行ストアテーブルで構成されるデータウェアハウスがあります。これらのテーブルは分析のみに使用され、CCIデータはステージングテーブルから15分ごとに挿入されます。パーティションを追加して並べ替えることにより、クエリのパフォーマンスを最適化したいと考えています。
このデータのすべてのクエリは、約350の異なる値を持つ整数フィールドに基づいています。左端のCCIには、100Mレコードと125列があります。同じ整数フィールドを持つ3つの子CCIがあります。 CCI 2には15Mのレコードと150列があり、CCI 3と4には両方とも約30Mのレコードと25列があります。
これら350個の異なる整数のうち、左端のテーブルのレコード数の分布は次のとおりです。
さらに、CCIに結合する他の9つの行ストアテーブルがあります。これらには細流の挿入があり、CCIの子であり、すべて同じ整数フィールドを含んでいます。これらの行ストアのレコードボリュームは類似しているか、それより小さく、それぞれ10列未満で、2つはLOBSを含み、2つは頻繁に大量更新されます(これらの更新はIDフィールドにも基づいています)。
いくつのパーティションを作成する必要がありますか?
行ストアテーブルもパーティション化する必要がありますか?
見落としている重要な考慮事項はありますか?
前に述べた「ソート」に関する注意:
左端のCCIの日付フィールドは、多くの場合、これらのクエリの二次述語であるため、メンテナンスとして、4週間ごとなどにそのCCIを日付で再ソートすることを検討しています。 CCIを削除し、日付にクラスター化された行ストアインデックスを追加し、そのインデックスを削除してから、MAXDOP = 1でCCIを再度追加することで、この並べ替えを実現します。また、子CCIを親への結合キーで並べ替えることも検討しています。
パーティション分割を本番環境に移行した後の更新:
クラスター化列ストアインデックス(CCI)の適切なパーティション分割を決定することは、非常に特注のプロセスです。間違ったパーティションが選択されると、パフォーマンスと圧縮は、パーティション化されていないスキームよりも悪くなります。
4つのCCIをパーティション分割していたため、レコード数が最も少ないCCIを選択し、そのレコード数を1,048,576(理想的なCCI行グループサイズ)で割りました。私は提案されたパーティション数としてその商を使用しました。次に、そのスキームに基づいてレコードカウントクエリを実行し、パーティションごとの実際の行数を返しました。このステップは、パーティション間でレコードが合理的に均等に分散されていることを確認するためのものでした。ありました。私は幸運。
障害が発生しました。このプロダクション分析プロセスにより、正しい数のパーティションにたどり着くことができました。私の下位環境は、本番環境よりもはるかに小さいです。選択したパーティションレベルでデータを細かくスライスしたため、完全な行グループはまったくありませんでした。下位のデータベースは大きくなり、クエリ時間は同じままでした。 IOは劇的に低下しました。このイニシアチブの成果が疑問視されているため、繰り返し指摘する必要がありました。パーティション分割が実際に役立つまで、実際に役立つことを証明するのは困難でした。
結果:パーティション分割は、本番環境で大成功を収めています。 IOはかなりダウンしていて、クエリ時間が70%以上短縮されました。これらのテーブルを小さなチャンクで管理するためのオプションも増えました。
注意事項:パーティション化する正しいフィールドを選択してください。多数のパーティションをナビゲートする必要があるクエリがある場合、パフォーマンスが低下することがあります。また、私には拡張の余地があり、現在は存在しないが1日になるデータのパーティション関数にパーティションと範囲を追加しています。
ローカルテストのみからの元の回答:
この質問をして以来、私は現地でより多くの研究とPOCを行ってきました。このPOCを回答で共有するよう提案されました。
私のPOCでは、次のパーティション関数を使用することにしました。
CREATE PARTITION FUNCTION [MyIntPF](int)
AS RANGE LEFT
FOR VALUES (
N'50'
, N'100'
, N'150'
, N'200'
, N'250'
, N'300'
, N'350'
, N'400'
, N'450'
, N'500'
);
CREATE PARTITION SCHEME [MyIntPS]
AS PARTITION [MyIntPF]
TO (
[MyInt050fg]
, [MyInt100fg]
, [MyInt150fg]
, [MyInt200fg]
, [MyInt250fg]
, [MyInt300fg]
, [MyInt350fg]
, [MyInt400fg]
, [MyInt450fg]
, [MyInt500fg]
, [MyInt000fg]
);
この関数は、50のMyIntsを各パーティションに割り当て、少し拡張する余地を与えます。
PROD CCIの1億7千万のレコードに約350の異なるMyIntsがあることに注意してください。 David Browneは、CCI圧縮セグメントを最適化するために意味のある、パーティション内の最小レコードサイズ1Mを提案しました。私は2つの理由で大きくエラーになっています。最初の理由は、100パーティションのPOCモンスターを作成しないようにすることです。 2つ目は、パーティション内の各テーブルに1Mが適用されると想定していることです。私は4つの列ストアをパーティション化しています。そのうち最小のものは25Mのレコードを持っています。それを100個に分割すると、完全なセグメントを達成することはできません。
私のローカル開発DBでは、左端のCCIに220万レコードあり、子CCIのレコードよりもさらに少ないです。これは、現実的なPRODの複製を作成する場合に問題となります。私は本当にこれのために大きなローカルDBを作成するために少し余分な時間を優先する必要がありますが、当面は、前/後IOローカルパーティションの結果です。集約を照会しましたMyIntを前提とする私の左端のCCIから=単一の値。
パーティション化されていません
スキャンカウント1、論理読み取り0、物理読み取り0、先読み0、lob論理読み取り1548、 lob物理読み取り0、lob先読み44。 セグメント読み取り4、セグメント0をスキップしました。
分割
スキャンカウント1、論理読み取り0、物理読み取り0、先読み読み取り0、lob論理読み取り268、 lob物理読み取り0、lob先読み読み取り0。 セグメント読み取り1、セグメント0をスキップしました。
予想どおり、SQL ServerはMyInt等式述部を使用したクエリで、1つのパーティションを除くすべてのパーティションをスキップできました。
私はこれに取り組み続けており、物事が進行するにつれてここで更新する時間があるはずです。
CCIのパーティション分割の利点:
データがどのようにロードまたは変更されても、最低レベルの行グループの排除が保証されるため、クエリのパフォーマンスを向上させることができます。一般的なSQL Serverのパーティション分割に関するガイダンスのほとんどは、これを考慮していません。
パーティションレベルでの再構築やパーティションレベルでの再編成(パーティションの切り替え後)ができるという点で、メンテナンス操作の柔軟性が向上しています。別のパーティションを別のファイルグループに送信することもできますが、送信してもパフォーマンスが向上することはほとんどありません。ファイルグループはメンテナンス機能です。ファイル数を増やすと、パフォーマンスが向上する場合があります。ストレージの設定によっては、I/Oを改善するために、クエリに関連するデータを複数のファイルに分散させることがほぼ確実です。
パーティションの削除は、同じ列での行グループの削除よりも多くのシナリオをカバーします。たとえば、WHERE ID < 0 OR ID > 10
のフィルターは、行グループの削除には適していませんが、パーティションの削除には適しています。
パーティションによるループは、すべての行を変更する必要があるメンテナンス操作を実行するときに役立ちます。たとえば、テーブルの既存の列から派生できる新しい列をテーブルに追加するとします。パーティショニングにより、必要に応じてその作業をバッチに効率的に分割できます。
CCIのパーティション分割の欠点:
メンテナンスを行わないと、デルタ行グループの行数が劇的に増加する可能性があります。 MAXDOP 8で並列挿入を使用してロードされるパーティション化されていないCCIについて考えてみます。デルタストアには最大で4194304行があります。テーブルが50のパーティションを持つように変更された場合、デルタストアに209715200行を持つことが可能になりました。
列ストアへの挿入と削除のクエリプランには、DML演算子の子として並べ替え演算子を含めることができます。このソートで十分なメモリを確保できない場合、パフォーマンスが極端に低下する可能性があります。並列挿入を使用する場合は、一度に1つのパーティションのみを変更することをお勧めします。
パーティション機能を賢く選択しないと、パーティションが小さすぎる場合があります。多くの人々は、行グループの1048576行の制限を理想的なサイズとして指摘しますが、個人的には、そこに到達することの利点は誇張されていると思います。あなたがそれを助けることができるなら、あなたはおそらく多くの小さなパーティションを避けたいでしょう。
テーブルまたはデータベースにパーティションが多すぎると、問題が発生する可能性があります。残念ながら、これはあまり明確に定義されておらず、「パーティションが多すぎます」が実際に何を意味するのかについて信頼できるソースを見つけるのは困難です。クエリのコンパイル時間に関する問題を聞いて見ました。 最近の回答 がDBCC CHECKTABLE
についてもここにありました。
上記をあなたのシナリオに適用する:あなたが持っている行数で、あなたは本当に悪いケースのどれにも遭遇すべきではありません。クエリのパフォーマンスのために、一部の人々は本当に速いクエリ実行時間を必要とし、できるだけ多くの行グループをスキップする必要があります。クエリで実行される作業のほとんどは列ストアスキャンの範囲外であるため、他のユーザーは最低限の行グループの削除を必要とします。そのため、外部の誰かがパーティションの数を推奨することは困難です。 1億のテーブルの場合、4〜100の任意の値が妥当です。
パーティション内の行数が異なるクエリのいくつかをテストして、パフォーマンスがどのように変化するかを確認できます。これは、テーブルのコピーを作成するか、意図的な歪度を持つ1つのテーブルにパーティション関数を作成し、フィルタリングするIDを変更することでシミュレートできます。十分なクエリパフォーマンスが得られる結果を取得し、データの読み込みに問題がないことを確認したら、問題ありません。
Rowstoreは質問とは無関係であり、まったく別の質問です。パーティション化は、行ストアクエリのパフォーマンスを向上させるための適切なツールではありません。列ストアテーブルをパーティション化し、行ストアテーブルをそのままにするだけで、システムのパフォーマンスが向上しました。