パーティション射影の列挙型と挿入型の違いについてまとめてみた

2021.12.14

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

データアナリティクス事業本部の鈴木です。

パーティション射影の列挙型挿入型を検証してみて、違いが納得できるようになったのでまとめてみました。

人によるところかもしれませんが、私は「直接カテゴリカルな値をパーティションキーに指定する」という点で、パーティション射影の列挙型と挿入型が結構似ていて、使い分けが分からないな〜と思っていたため、記事にしてみました。

前提

パーティション射影とは

Amazon Athena(以降、Athena)では、パーティション射影(Partition Projection)という機能をサポートしています。パーティション射影を使うと、Athenaはクエリを実行するパーティションを選ぶ際に、パーティション値と場所のデータをGlueデータカタログなどのレポジトリに取りにいくのではなく、テーブル定義で設定したルールに基づいて自分で計算するようになります。これにより、クエリ実行のパフォーマンスの向上が期待できるほか、パーティション管理も自動化できます。

詳細は以下をご確認ください。

Amazon Athena でのパーティション射影 - Amazon Athena

列挙型と挿入型について

ユーザーが具体的なパーティション値を指定する型として、列挙型と挿入型があります。

1.列挙型

テーブルに列挙のメンバーを設定することで、パーティション値を解釈することができます。

2.挿入型

クエリのWHERE句で単一の値をパーティション値として指定することができます。

詳細は以下のドキュメントが参考になります。

パーティション射影のサポートされている型 - Amazon Athena

検証に使うデータ

cm-nayuts-athena-sampleバケットに、以下のようにparquetのデータが配置されているとします。

各parquetはtypeというカテゴリを表す値を持つ列でパーティションに分割されている想定です。

cm-nayuts-athena-sample/
└── type_example
    ├── type=A
    │   └── 0000_part_00.parquet
    ├── type=B
    │   └── 0000_part_00.parquet
    └── type=C
        └── 0000_part_00.parquet

データは以下のようなものをPythonスクリプトで生成し、typeカラムをパーティションに指定してparquetで出力しておきました。

id,year,month,day,data,type
1,2021,112021,2021-11-01,3.124831203897884,B
2,2021,112021,2021-11-02,8.324634779727319,C
3,2021,112021,2021-11-03,5.085190114639688,B
4,2021,112021,2021-11-04,4.846875401852115,A
5,2021,112021,2021-11-05,7.335101471946362,B

やってみる

テーブル設定の違い

上記データに対して、列挙型と挿入型のそれぞれでテーブルを作成してみます。

1.列挙型

列挙型では、projection.パーティション列名.typeenumを指定し、列挙のメンバをprojection.パーティション列名.valuesに記載します。

※SQL内の$データベース名は適当な値に置き換えてください。

sample_data_enum.sql

CREATE EXTERNAL TABLE IF NOT EXISTS $データベース名.sample_data_enum (
  year string, 
  month string, 
  day string, 
  data float
  )
PARTITIONED BY ( 
  type string)
ROW FORMAT SERDE 
  'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe' 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
LOCATION
  's3://cm-nayuts-athena-sample/type_example/'
TBLPROPERTIES (
  'has_encrypted_data'='false', 
  'projection.enabled'='true', 
  'projection.type.values'='A,B,C', 
  'projection.type.type'='enum',
  'storage.location.template'='s3://cm-nayuts-athena-sample/type_example/type=${type}'
)

2.挿入型

挿入型では、projection.パーティション列名.typeinjectedを指定します。

※SQL内の$データベース名は適当な値に置き換えてください。

sample_data_injected.sql

CREATE EXTERNAL TABLE IF NOT EXISTS ${DB名}.sample_data_injected (
  year string, 
  month string, 
  day string, 
  data float
  )
PARTITIONED BY ( 
  type string)
ROW FORMAT SERDE 
  'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe' 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
LOCATION
  's3://cm-nayuts-athena-sample/type_example/'
TBLPROPERTIES (
  'has_encrypted_data'='false', 
  'projection.enabled'='true', 
  'projection.type.type'='injected',
  'storage.location.template'='s3://cm-nayuts-athena-sample/type_example/type=${type}',
)

検索時の違い

作成したテーブルに対して、パーティション値を2つ指定して検索を実行してみましょう。

まず、列挙型を使ったテーブルです。 列挙型を使ったテーブルへのクエリ結果

次に、挿入型を使ったテーブルです。 挿入型を使ったテーブルへのクエリ結果

列挙型ではクエリが成功しましたが、挿入型では失敗することが分かります。

挿入型では必ず1つだけの値をパーティション値として指定する必要があるため、複数のパーティションを指定したい場合には利用できません。

使い分けについて

もちろんケースバイケースですが、以下を踏まえると使い分けしやすいと思ったのでご紹介します。

  • 列挙型では複数の値をパーティション値に指定できる代わりに、列挙のメンバーが増えると定義を修正する必要があります。初めからパーティション値が明確に列挙できる場合は列挙型を検討できます。
  • 挿入型ではDDLのメンテナンスをしなくて良い代わりに、常に単一のパーティション値をWHERE句で指定することになります。パーティション値の候補が動的に追加され、そのような制限があっても気にしない場合は、挿入型を検討できます。

なお、以下も重要ですので、念頭に置いておきましょう。

最後に

今回はパーティション射影の列挙型と挿入型の違いについてまとめました。どちらの機能も非常に便利なので、日々の開発に取り入れていければ良いですね!