この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
データアナリティクス事業本部の鈴木です。
パーティション射影の列挙型
と挿入型
を検証してみて、違いが納得できるようになったのでまとめてみました。
人によるところかもしれませんが、私は「直接カテゴリカルな値をパーティションキーに指定する」という点で、パーティション射影の列挙型と挿入型が結構似ていて、使い分けが分からないな〜と思っていたため、記事にしてみました。
前提
パーティション射影とは
Amazon Athena(以降、Athena)では、パーティション射影(Partition Projection)という機能をサポートしています。パーティション射影を使うと、Athenaはクエリを実行するパーティションを選ぶ際に、パーティション値と場所のデータをGlueデータカタログなどのレポジトリに取りにいくのではなく、テーブル定義で設定したルールに基づいて自分で計算するようになります。これにより、クエリ実行のパフォーマンスの向上が期待できるほか、パーティション管理も自動化できます。
詳細は以下をご確認ください。
列挙型と挿入型について
ユーザーが具体的なパーティション値を指定する型として、列挙型と挿入型があります。
1.列挙型
テーブルに列挙のメンバーを設定することで、パーティション値を解釈することができます。
2.挿入型
クエリのWHERE句で単一の値をパーティション値として指定することができます。
詳細は以下のドキュメントが参考になります。
検証に使うデータ
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.パーティション列名.type
にenum
を指定し、列挙のメンバを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.パーティション列名.type
にinjected
を指定します。
※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句で指定することになります。パーティション値の候補が動的に追加され、そのような制限があっても気にしない場合は、挿入型を検討できます。
なお、以下も重要ですので、念頭に置いておきましょう。
- 列挙型のドキュメントでは注記として、メンバーの数は10以下が推奨されている記載があります。それを超える場合は、カーディナリティが低くなるように工夫する必要があります。列挙型が実用的でない場合の対応は『ID の動的なパーティション化』のドキュメントが参考になります。
-
いずれの場合も、「カーディナリティが高く、値が均等に分散されている列」でパーティション分割するなら、パーティション分割ではなくバケット化を検討しても良いかもしれません。詳細は『バケット化とパーティション分割の比較 - Amazon Athena』をご確認ください。
最後に
今回はパーティション射影の列挙型と挿入型の違いについてまとめました。どちらの機能も非常に便利なので、日々の開発に取り入れていければ良いですね!