web-dev-qa-db-ja.com

マルチテナントデータベースの列ストアインデックス

分析にのみ使用されるSQL Azureデータベースがあります。

OLTPシステムからプッシュされたデータで常に更新されています。

OLTP inserts/updates/deletesは、変更をリアルタイムで分析データベースに伝達するサービスにWebhook経由でプッシュされます。

その結果、OLTPと分析作業負荷の両方を維持する必要があるデータベースがありますが、分析クエリのパフォーマンスはより重要です。

以前に列ストアインデックスを使用したことがありませんが、最小限の調査を行った後、このタイプのペイロードは非クラスター化列ストアインデックスに適しているようです。クラスター化されたインデックスは、挿入、更新、削除を高速化するために、行ストアにとどまります。

しかし、私は言及していないように見える追加の要件があります。それはマルチテナンシーです。ユーザーが分析アプリを使用する場合、ユーザーは常に単一のテナントIDに関連付けられ、分析クエリには常にテナントIDのWHERE句が含まれます。テナントID列は非常に選択的です。最大のテナントはすべての行の5%未満を占め、ほとんどのテナントは行の1%未満を占めます。

マルチテナントデータベースにNCCIインデックスを使用することはまだ良い考えですか?その場合、特定の方法でインデックスを設定する必要がありますか?

4
Clement

マルチテナントデータベースにNCCIインデックスを使用することはまだ良い考えですか?

代替策がテナントによって効率的にフィルタリングするデータ構造を持つことである場合、そうではありません。

TenantIdがクラスター化インデックスの主要な列である場合、シングルテナントのクエリはかなり高速になります。 NCCI行グループはテナントによって分離されないため、単一のテナントの行を検索するには、すべての行グループをスキャンする必要があります。

行が挿入されると、それらはクラスター化インデックスの「中央」、通常はテナントの行の「終わり」に挿入されます。ただし、行は常にCCI/NCCIのデルタストアに挿入されます。また、デルタストアに100万行ある場合は常に、列の行グループに再構築されます。したがって、各行グループには、最後の100万行がすべてのテナントに挿入されます。

これは、TenantIDによるパーティション分割で修正できます。これにより、各テナント(またはテナントのグループ)に個別の物理テーブル(またはNCCI)を与えることができます。各パーティションには独自のデルタストアがあり、シングルテナント行でいっぱいになります。パーティションを作成すると、各パーティションが小さすぎて列ストアが役に立たなくなる可能性があります。少なくともパーティションごとに数百万行が必要です。

一般に、マルチテナントデータでは、単一のテナントのデータを取得するためにすべてのテナントデータを読み取る必要があるような方法で、テナントデータをインターリーブしないようにする必要があります。

これは答えではありませんが、代わりに、私のコメントの(うまくいけば)より良い説明がDavid Browneの(はるかによく、うまくいけば受け入れられます)答えに残されました。

最初に、Davidの答えは私の意見では最良のアプローチであり、彼はマルチテナントデータベース設計の主要な懸念に迅速に対応する素晴らしい仕事をしています。 パーティショニングを使用してください!使用してください!パーティショニングはここで適切な機能であり、NCCIでも機能します。

フィルター処理されたNCCIを使用してNCCIパフォーマンスをさらに向上させることについての私のコメントは、パーティション化の代わりとして意図されたものではなく、(データの動作に応じて)フィルター処理されたNCCIを使用するパーティション分割に加えて。フィルター処理されたNCCIは、以下が当てはまる場合に適したオプションです。

  1. NCCIを検討している特定のテーブルの場合、このテーブルには、テナントごと、週ごとに100万を超えるレコードが挿入されていませんありません。あなたの質問に対する私の最初のコメントへの回答によれば、これはあなたのシナリオに当てはまるようです。
  2. 同じテーブルの場合、その定義に含まれる1つまたは複数の列があり、新しい/揮発性データまたは古い/安定したデータを意味します。これは日付フィールドまたは列の組み合わせである可能性がありますが、基本的にこの列はフィルター条件として使用され、データの変動性を表す列の組み合わせが存在しない場合、フィルター処理されたNCCIを適切に定義することがより困難になります。したがって、あまり役に立ちません。

まず、フィルター処理されたNCCIの優れている点は何ですか。

フィルター処理されたNCCIは、クエリによって返されるデータがフィルター処理されたNCCI内に完全に含まれていない場合でも、エンジンがそのデータを使用してできるデータを抽出します次に(従来の)残りのデータを取得するための行ストアインデックス。これは、NCCIが対応する行ストアインデックスを実行する傾向がある分析/集計ワークロードに関して特に役立ちます。

この概念は このMS記事でよりよく説明されています しかし、この記事から引用したこの情報グラフィックは、すべてをまとめるのに非常に効果的であることがわかりました。

enter image description here

NCCIを使用することの欠点は何ですか?

NCCIが作成された後、RowGroup 内に格納されているデータは実質的に読み取り専用になる になることを理解することが重要です。変更はRowGroup自体には適用されず、レコードは削除済みとしてマークされ、更新されたデータはNCCI内の別のRowGroupに保存されます。これらの変更は Deleted BufferとDeleted Bitmap にカタログ化されているため、エンジンは特定のRowGroupで有効なものを認識しています。適切なメンテナンスは、データ変更のパフォーマンスコストを最小限に抑えるのに役立ちますが、メンテナンスウィンドウ間のテーブル内の最新データ(たとえば、「ホット」データ)で多くのアクティビティが発生している場合、フィルター処理されたNCCIがセグメント化する最良の方法である可能性があります揮発性が高くホットなデータからの揮発性が低く暖かいデータ。

仮定が重要なのはなぜですか?

最初の仮定が偽であり、テナントごとに1週間あたり100万を超えるレコードがテーブルに挿入されている場合、フィルターされたNCCIを無視できます。代わりに、追加のCOMPRESSION_DELAYキーワードを使用してNCCIを作成する必要があります。これを実行する前にMSが実行することを推奨するクエリ found here があります。これにより、この値がどのようになるかがよくわかりますが、最大値は10080または7日間です。これにより、100万レコードが蓄積されるか、指定された期間が経過するまで、デルタストアが開いたままになり、その後、そのデルタストアはNCCI RowGroupに変換されます。これにより、基本的に、NCCIはスライディングウィンドウでフィルター処理されたNCCIのように自動的に機能します。ただし、この使用例を利用するためにRowGroupを定期的に埋めるのに十分な受信データがない場合は、フィルター処理されたNCCIがより良いアプローチである可能性があります。

2番目の仮定が偽であり、どのデータがウォームvsホットかを簡単に定義できる列がない場合、フィルター処理されたNCCIは通常のNCCIと同じになります。ここでも、適切な値を指定してCOMPRESSION_DELAYキーワードを使用し、そこから移動します。

フィルター処理されたNCCIのコスト

フィルター処理されたNCCIにはコストがかかりません。主な問題はフィルターの定義です。ホットデータは最終的にウォームデータになるので、フィルターを調整して、この追加データをフィルター処理されたNCCIに含めることができます。 ALTER INDEXを使用してNCCIのフィルターを変更することはできないため、フィルターの定義を変更するには、インデックスを削除し、更新されたフィルター句を使用して再作成する必要があります。これは明らかに、この変更中はインデックスにアクセスできなくなるという悪影響をもたらします。そのため、データベースがこの種の操作に十分な長いメンテナンスウィンドウを提供できない場合は、フィルター処理されたNCCIを使用しないでください。フィルター処理されたNCCIには、 ここで完全に概説 として他のいくつかの技術的な制限もあります。

フィルター処理されたNCCIの利点は、新しいデータが非常に変動しやすく、NCCIが対応する行ストアインデックスよりもパフォーマンスを向上させていない場合のコストを上回ります。ここでのテストはゲームの名前ですが、適切な状況では、マルチテナントデータベースのパーティションテーブルの上に置かれたフィルター処理されたNCCIは、パフォーマンスの大幅な向上につながります。

うまくいけば、Davidの以前の答えに対する私のコメントを説明するのにより良い仕事をしてくれるといいのですが...これも答えではなく、非常に長い時間のコメントです。

2
John Eisbrener