データパーティション分割スキームを設計して実装する
データのパーティション分割は、クエリの実行効率と消費するコンピューティング リソースの量に直接影響します。 テーブルをパーティション分割すると、列の値に基づいてデータを個別のセグメントに分割し、クエリ エンジンがデータセットの関連部分のみを読み取れるようにします。 パーティションの排除と呼ばれるこの方法では、フィルターがパーティションの境界と一致する場合に、データ スキャンを大幅に削減できます。
何百万もの日次販売トランザクションを処理している小売企業を考えてみましょう。 アナリストは、データを日付範囲で照会して週単位のレポートを生成し、月ごとの傾向を比較し、季節的なパターンを分析することが多い。 パーティション分割を行わないと、すべてのクエリでトランザクション履歴全体がスキャンされます。 日付による適切なパーティション分割では、特定の週に対するクエリは、保存されているファイルのほんの一部に過ぎません。
パーティション分割が適用されるタイミングを決定する
Databricks では、 1 TB 未満のデータを含むテーブルをパーティション分割しないことをお勧めします。 小さいテーブルの場合、パーティション メタデータを管理するオーバーヘッドと、多数の小さなファイルを作成する可能性が、クエリ パフォーマンスの利点を上回ります。 最近の Databricks ランタイム バージョンのパーティション分割されていない Delta テーブルは 、インジェスト時間クラスタリングの恩恵を自動的に受けます。これは、明示的な構成なしで同様のクエリの利点を提供します。
テーブルが 1 TB を超えると、パーティション分割は重要な最適化手法になります。 この規模では、関連するデータ セグメントのみを読み取る利点によって、パーティション管理のコストが大幅に相殺されます。 また、Spark は同時実行のためにクラスター ノード間で異なるパーティションを分散できるため、パーティション分割によって並列処理が可能になります。
Important
各パーティションには、少なくとも 1 GB のデータが含まれている必要があります。 このガイドラインは、過剰なパーティション分割を回避するのに役立ちます。これにより、小さなファイルが過剰に作成され、クエリのパフォーマンスが低下します。
数百テラバイトの範囲の非常に大きなテーブルでは、パーティション分割によりさらに大きな利点が得られます。 この規模では、パーティションの排除により、クエリ エンジンが処理する必要があるデータの量が大幅に削減され、パフォーマンスとコスト効率の両方が向上します。
有効なパーティション キーを選択する
パーティション キーを選択するには、データの特性とクエリ パターンを理解する必要があります。 効果的なパーティション キーは、一般的な落とし穴を回避しながら、パフォーマンスの利点を最大化する共通の特性を共有します。
カーディナリティの低い列が最適に動作する
個別の値の数が限られている列を選択します。 日付列、地理的リージョン、製品カテゴリ、および状態フィールドは、管理しやすい数のパーティションを作成するため、優れたパーティション キーを作成します。 顧客 ID やトランザクション ID など、数千または数百万の一意の値を持つ列は、パーティション分割が過剰になり、パフォーマンスが低下します。
たとえば、日次レベルで sale_date して売上テーブルをパーティション分割すると、1 年あたり 365 個のパーティションが作成される場合があります。 毎日十分なデータ 量が含まれている場合、この細分性は適切に機能します。 ただし、2 番目のレベルで sale_timestamp によるパーティション分割では、数百万個のパーティションが作成され、それぞれに最小限のデータが含まれます。
クエリ パターンに合わせる
パーティション キーは、ユーザーが最も頻繁にフィルター処理する列と一致する必要があります。 アナリストが日付範囲で売上データのクエリを一貫して実行する場合、日付によるパーティション分割により効率的な排除が可能になります。 クエリが主にリージョン別にフィルター処理される場合は、代わりにリージョンをパーティション キーと見なします。
複数のフィルター パターンが存在する場合は、最も一般的または最もパフォーマンスが重要なクエリに優先順位を付けます。 テーブルごとに 1 つのパーティション構成を定義できるため、ワークロードの大部分にメリットがある列を選択します。 クエリ パターンが大幅に異なる場合は、特定のユース ケース用に最適化された 具体化されたビュー または サマリー テーブル を個別に作成することを検討してください。
時間の経過に伴うデータの増加を検討する
データの拡大に合わせて適切にスケーリングするパーティション構成を選択します。 年別のパーティション分割は最初は適切に機能しますが、時間の経過と同時に大きすぎるパーティションが作成される可能性があります。 逆に、時間単位のパーティション分割は大量のデータに対して機能する可能性がありますが、中程度のボリュームでは小さなパーティションが多すぎます。
パーティションの粒度を選択するときに、予想されるデータ増加率を評価します。 パーティション サイズとパーティション数のバランスは、データセットの拡張に合わせて一貫したクエリ パフォーマンスを維持するのに役立ちます。
また、パーティション間でのデータ分散も検討してください。 大きなパーティションを処理するノードは完了するまでに時間がかかり、他のノードはアイドル状態のままで済むので、パーティション サイズが大きく不均等な場合、処理のボトルネックが発生する可能性があります。
Azure Databricks でパーティションを実装する
パーティションは、SQL の PARTITIONED BY 句または PySpark の partitionBy メソッドを使用してテーブルを作成するときに定義します。 パーティションを確立すると、Azure Databricks は、列の値に基づいて受信データを適切なパーティション ディレクトリに自動的にルーティングします。
SQL を使用してパーティション テーブルを作成する
次の例では、トランザクション日付でパーティション分割された販売トランザクション テーブルを作成します。
CREATE TABLE sales.transactions (
transaction_id STRING,
customer_id STRING,
product_id STRING,
quantity INT,
unit_price DECIMAL(10,2),
total_amount DECIMAL(12,2),
transaction_date DATE
)
PARTITIONED BY (transaction_date);
日付フィルターを使用してこのテーブルにクエリを実行すると、クエリ エンジンはフィルター条件に一致するパーティションのみを読み取ります。
SELECT product_id, SUM(total_amount) AS revenue
FROM sales.transactions
WHERE transaction_date BETWEEN '2024-01-01' AND '2024-01-31'
GROUP BY product_id;
PySpark を使用してパーティション テーブルを作成する
PySpark を使用する場合は、書き込み操作中にパーティション分割を指定します。
df.write.format("delta") \
.partitionBy("transaction_date") \
.saveAsTable("sales.transactions")
後続の書き込みでは、同じパーティション仕様を含めることで整合性を維持します。
new_transactions_df.write.format("delta") \
.mode("append") \
.partitionBy("transaction_date") \
.saveAsTable("sales.transactions")
パーティションの柔軟性のために生成された列を使用する
生成された列は、冗長データを格納せずに既存の列からパーティション値を派生させる方法を提供します。 この方法は、ソース データにタイムスタンプが含まれているのに日付でパーティション分割する場合に便利です。
CREATE TABLE sales.transactions (
transaction_id STRING,
customer_id STRING,
product_id STRING,
quantity INT,
unit_price DECIMAL(10,2),
total_amount DECIMAL(12,2),
transaction_timestamp TIMESTAMP,
transaction_date DATE GENERATED ALWAYS AS (CAST(transaction_timestamp AS DATE))
)
PARTITIONED BY (transaction_date);
ヒント
この構成では、基本タイムスタンプ列を使用してクエリを実行すると、Delta Lake によってパーティション フィルターが自動的に生成されます。
transaction_timestampに対するクエリ フィルター処理は、明示的な日付フィルターを必要とせずにパーティションを排除する利点があります。
一般的なパーティション分割の間違いを回避する
何を避けるかを理解することは、何をすべきかを知るのと同じくらい重要です。 いくつかの一般的な間違いにより、パフォーマンスが大幅に低下し、データ管理が複雑化する可能性があります。
パーティション分割を過剰に行うと、小さなファイルの問題が発生する
不十分なデータを含むパーティションが多すぎると、 小さなファイルの問題が発生します。 パーティションにターゲット ファイル サイズより小さいファイルが含まれている場合、クエリのパフォーマンスは過剰なメタデータ操作と読み取り効率の低下に苦しみます。 少なくとも 1 GB のデータを含むパーティションを目指します。
パーティショニングの過剰兆候には、パーティションプルーニングにもかかわらずクエリのパフォーマンスが低下すること、パーティションディレクトリ内の非常に多くの小さなファイル、長時間かかるテーブルの最適化などがあります。 このような現象が発生した場合は、より粗いパーティション粒度を選択することを検討してください。
カーディナリティの高い列でのパーティション分割が失敗する
一意の値が多い列では、パーティション キーが不適切になります。 顧客 ID、トランザクション ID、またはその他の高カーディナリティの列でパーティション分割すると、何百万ものパーティションが生成され、パフォーマンスが低下します。 この目的のために設計された他の手法に対して、高カーディナリティ列の最適化は専用にしてください。
パーティションを変更するには、データの書き換えが必要です
パーティション列はテーブル構造の一部です。 パーティション分割戦略を変更するには、異なるパーティション境界を持つ新しい場所に テーブル全体を書き換える 必要があります。 この操作では、大規模なテーブルに対して大量のコンピューティング リソースと時間が消費されます。
大規模なデータを読み込む前に、パーティション分割戦略を慎重に計画してください。 後でコストのかかる再構築を回避するために、今後のクエリ パターンとデータの増加を検討してください。
パーティション分割の決定を文書化する
パーティション キーの選択とその背後にある理由を記録します。 このドキュメントは、チーム メンバーがクエリの最適化の機会を理解し、パフォーマンスに影響を与える可能性のある偶発的な変更を防ぐのに役立ちます。 予想されるパーティション サイズ、スキームの恩恵を受けるクエリ パターン、および決定に影響を与える制約に関する情報を含めます。
代替手段として液体クラスタリングを検討する
パーティション分割の課題 (特にタイムスタンプなどの カーディナリティの高い列 やクエリ パターンが 不明な場合) が発生した場合、液体クラスタリングは最新の代替手段となります。 従来のパーティション分割とは異なり、液体クラスタリングでは次の機能が提供されます。
- データを書き換えずに最適化戦略を調整する柔軟性
- テーブルが拡大する際の自動メンテナンス
- パーティション管理のオーバーヘッドの削減
新しいテーブルでは、パーティション分割の制約によってパフォーマンスが制限されたり、過剰なメンテナンスの負担が発生したりする場合に、液体クラスタリングを 使用することをお勧めします 。 特に、1 TB を超えるテーブルで、従来の方法を使用してパーティションが多すぎる列をフィルター処理する必要がある場合に有効です。
注
液体クラスタリングは、パーティション テーブルと互換性がありません。 新しいテーブルを設計するときに、要件に基づいて 1 つのアプローチを選択します。
実装の詳細、構文、および最適化戦略については、このモジュールの後半の 「クラスタリング戦略ユニットの設計と実装 」を参照してください。