Athena におけるパーティション化とバケット化 - Amazon Athena

Athena におけるパーティション化とバケット化

クエリを実行する際に Athena がスキャンする必要があるデータ量を減らすための 2 つの方法として、パーティション化とバケット化があります。パーティション化とバケット化は補完的な機能で、併用できます。スキャンするデータ量を減らすことは、パフォーマンスの向上とコストの削減につながります。Athena クエリのパフォーマンスに関する一般的なガイドラインについては、「Amazon Athena のパフォーマンスチューニング Tips トップ 10」を参照してください。

パーティション化とは

パーティション化とは、データの特定のプロパティに基づいて Amazon S3 上にあるディレクトリ (または「プレフィックス」) にデータを整理することを意味します。このようなプロパティはパーティションキーと呼ばれます。一般的なパーティションキーは、日付またはその他の時間単位 (年や月など) です。ただし、データセットは複数のキーでパーティション化できます。たとえば、製品の売上に関するデータは、日付、製品カテゴリ、市場ごとにパーティション化できます。

パーティション化する方法の決定

パーティションキーに適しているのは、クエリで常にまたは頻繁に使用され、カーディナリティが低いプロパティです。パーティションの数が多すぎる/少なすぎることについては、それぞれの欠点があります。パーティションが多すぎると、ファイル数が増えるとオーバーヘッドが発生します。また、パーティション自体をフィルタリングすることによるオーバーヘッドもあります。パーティションが少なすぎると、クエリがより多くのデータをスキャンする必要性が多発します。

パーティションテーブルの作成

データセットをパーティション化すると、Athena でパーティションテーブルを作成できます。パーティションテーブルは、パーティションキーを含むテーブルです。CREATE TABLE を使用すると、テーブルにパーティションを追加します。CREATE TABLE AS を使用すると、Amazon S3 上で作成されたパーティションがテーブルに自動で追加されます。

CREATE TABLE ステートメントでは、PARTITIONED BY (column_name data_type) 句にパーティションキーを指定します。CREATE TABLE AS ステートメントでは、WITH (partitioned_by = ARRAY['partition_key']) 句に、または Iceberg テーブルの WITH (partitioning = ARRAY['partition_key']) にパーティションキーを指定します。パフォーマンス上の理由から、パーティションキーは常に STRING タイプにする必要があります。詳細については、「パーティションキーのデータ型として文字列を使用」を参照してください。

その他の CREATE TABLE および CREATE TABLE AS 構文の詳細については、「CREATE TABLE」と「CTAS テーブルのプロパティ」を参照してください。

パーティションテーブルのクエリ

パーティションテーブルをクエリすると、Athena はクエリ内の述語を使用してパーティションのリストをフィルタリングします。次に、一致するパーティションの場所を使用して、見つかったファイルを処理します。単純に、クエリ述語と一致しないパーティションのデータを読み取らないようにすることで、Athena がスキャンするデータ量を効率的に減らせます。

テーブルを sales_dateproduct_category でパーティション化していて、特定のカテゴリにおける 1 週間の総収益を知りたいとします。次の例のように、Athena がスキャンするデータ量が最小限になるように、sales_date 列と product_category 列に述語を含めます。

SELECT SUM(amount) AS total_revenue FROM sales WHERE sales_date BETWEEN '2023-02-27' AND '2023-03-05' AND product_category = 'Toys'

日付別にパーティション化されているが詳細なタイムスタンプも含むデータセットがあるとします。

Iceberg テーブルではパーティションキーを宣言して列に関係を構築できますが、Hive テーブルではクエリエンジンは列とパーティションキーの関係に関するデータを持っていません。。このため、クエリが必要以上に多くのデータをスキャンしないように、クエリにある列とパーティションキーの両方に述語を含める必要があります。

たとえば、前の例における sales テーブルにも TIMESTAMP データ型の sold_at 列があるとします。特定の時間範囲の収益のみを求める場合は、クエリを次のように記述します:

SELECT SUM(amount) AS total_revenue FROM sales WHERE sales_date = '2023-02-28' AND sold_at BETWEEN TIMESTAMP '2023-02-28 10:00:00' AND TIMESTAMP '2023-02-28 12:00:00' AND product_category = 'Toys'

Hive テーブルと Iceberg テーブルのクエリの違いに関する詳細については、「同じく時間分割されているタイムスタンプフィールドのクエリを作成する方法」を参照してください。

バケット化とは

バケット化は、データセットのレコードをバケットと呼ばれるカテゴリに整理する方法です。

この意味におけるバケットバケット化は Amazon S3 バケットとは異なるため、混同しないでください。データバケットでは、プロパティと同じ値を含むレコードが同じバケットに入ります。レコードはバケット間で可能な限り均等に分散されるため、各バケットにはほぼ同じ量のデータが含まれます。

実際には、バケットはファイルであり、ハッシュ関数がレコードが入るバケットを決定します。バケット化されたデータセットには、パーティション/バケットごとに 1 つ以上のファイルがあります。ファイルが属するバケットはファイル名でエンコードされます。

バケット化のメリット

バケット化は、データセットが特定のプロパティによってバケット化されており、そのプロパティに特定の値があるレコードを取得する際に役立ちます。データはバケット化されているので、Athena は検索するファイルを決定する値を使用できます。たとえば、データセットが customer_id 別にバケット化されていて、特定の顧客に関するすべてのレコードを検索したいとします。Athena はそれらのレコードを含むバケットを特定し、そのバケット内にあるファイルのみを読み取ります。

高基数 (つまり異なる値が多い) 列が均一に分散していて特定の値を頻繁にクエリする列がある際に、バケット化に適している対象が見つかります。

注記

Athena は、INSERT INTO を使用してバケットテーブルに新しいレコードを追加する機能はサポートしていません。

バケット化された列でのフィルタリングでサポートされるデータ型

特定のデータ型を持つバケット列にフィルターを追加できます。Athena は、次のデータ型を持つバケット化された列に対してフィルタリングをサポートしています:

  • BOOLEAN

  • BYTE

  • DATE

  • DOUBLE

  • FLOAT

  • INT

  • LONG

  • SHORT

  • STRING

  • VARCHAR

Hive と Spark のサポート

Athena エンジンバージョン 2 は Hive バケットアルゴリズムを使用するバケット化されたデータセットをサポートし、Athena エンジンバージョン 3 は Apache Spark バケットアルゴリズムもサポートしています。Hive バケット化はデフォルトです。データセットが Spark アルゴリズムを使用してバケット化されている場合は、TBLPROPERTIES 句を使用して bucketing_format プロパティの値を spark に設定します。

注記

Athena では、CREATE TABLE AS SELECT (CTAS) クエリ内のパーティション数は 100 個に制限されています。同様に、INSERT INTO ステートメントを使用すると、宛先テーブルに最大 100 個のパーティションのみを追加できます。この 100 個の制限は、テーブルのバケット化とパーティション化の両方が行われている場合にのみ適用されます。

この制限を超えると、HIVE_TOO_MANY_OPEN_PARTITIONS: Exceeded limit of 100 open writers for partitions/buckets (HIVE_TOO_MANY_OPEN_PARTITIONS: パーティション/バケットのオープンライターの制限である 100 を超えました) エラーメッセージが表示されることがあります。これらの制限を回避するには、CTAS ステートメントと、それぞれ最大 100 個のパーティションを作成または挿入する一連の INSERT INTO ステートメントを使用できます。詳細については、「CTAS および INSERT INTO を使用して 100 パーティションの制限を回避する」を参照してください。

CREATE TABLE のバケット化に関する例

既存のバケット化されたデータセット用のテーブルを作成するには、CLUSTERED BY (column) 句の後に INTO N BUCKETS 句を続けて使用します。INTO N BUCKETS 句は、データをバケット化する先のバケットの数を指定します。

次の CREATE TABLE の例では、sales データセットは customer_id 別に、Spark アルゴリズムを使用して 8 つのバケットにバケット化されています。この CREATE TABLE ステートメントでは、CLUSTERED BY 句と TBLPROPERTIES 句を使用してプロパティをそれぞれ設定します。

CREATE EXTERNAL TABLE sales (...) ... CLUSTERED BY (`customer_id`) INTO 8 BUCKETS ... TBLPROPERTIES ( 'bucketing_format' = 'spark' )

CREATE TABLE AS (CTAS) のバケット化に関する例

CREATE TABLE AS のバケット化を指定するには、bucketed_by パラメータと bucket_count パラメータを次の例のように使用します。

CREATE TABLE sales WITH ( ... bucketed_by = ARRAY['customer_id'], bucket_count = 8 ) AS SELECT ...

クエリのバケット化に関する例

次のクエリ例では、特定の顧客が 1 週間を通じて購入した製品の名前を検索します。

SELECT DISTINCT product_name FROM sales WHERE sales_date BETWEEN '2023-02-27' AND '2023-03-05' AND customer_id = 'c123'

このテーブルを sales_date 別にパーティション化して customer_id 別にバケット化すると、Athena は顧客レコードが入っているバケットを計算できます。Athena が読み込めるのは 1 パーティションにつき 1 ファイルまでです。

以下も参照してください。

バケット化されたテーブルとパーティション化されたテーブルの両方を作成する CREATE TABLE AS の例については、「例: バケット化およびパーティションされたテーブルを作成する」を参照してください。