[AWS] Glueパーティションインデックスを有効に利用する上で確認しておきたいポイント

# はじめに

データ量の増大やデータ自体の多様化に伴い、データストレージとコンピューティングリソースを分離したデータレイクハウスが注目されてきています。
一般的に、データウェアハウス (DWH) に比べてランニングコストが安いオブジェクトストレージに置かれたデータを、DWH や DB 等と同じような感覚でクエリ可能とし、クエリした分だけオンデマンド課金される仕組みによってビッグデータの利活用のコストハードルが格段に下がりました。
しかし、コストハードルが下がった分、データクエリに最適化されていない安価なストレージに置かれたデータを一度スキャンした上でクエリ処理を行う必要があり、データスキャンのパフォーマンスが課題となります。

AWS サービスのみでデータレイクハウスを構成する際、ストレージとして S3、カタログ管理として Glue、クエリエンジンとして Athena や Redshift Spectrum の組み合わせが一般的です。
加えて、Glue の機能であるパーティションインデックスは実際のS3パーティション構造とは別のパーティション構造を持つインデックスを作成することによって、データスキャンのパフォーマンスを改善してくれます。
しかし、パーティションインデックスを安易に導入しても効果がないどころかコスト的に逆効果になってしまうケースもあるため、本記事ではパーティションインデックスを有効利用する上で確認していきたいポイントをまとめます。


# 1. (将来分も含めて)パーティション数の上限緩和が必要か

パーティションインデックスが直接的な原因ではないですが、Glueでカタログ管理する以上パーティション数のクオータを意識する必要があります。
2024年12月19日時点、[(公式) AWS Glue エンドポイントとクォータ](https://docs.aws.amazon.com/general/latest/gr/glue.html) によるとデフォルトでは**テーブルあたり 10,000,000 パーティション**、**アカウントあたり 20,000,000 パーティション**が上限とされています。

例えば、 `/country=JP/year=2024/month=12/day=19/hour=10/data.parquet` のようなパーティション構造の場合、 `country` が193ヶ国 (2024年12月19日時点の国連加盟国数) だとすると1年間で最大約170万パーティションが必要になります。
もし10年分のデータをテーブル化する場合は、上限緩和を忘れずにしましょう。


# 2. パーティションインデックスのメリットが薄いクエリパターンとパーティション構造になっていないか

以下、パーティションインデックスのメリットが薄い例を示します。
例を参考に、パーティションインデックスを設定するメリットが本当にあるのかイメージできるかと思います。

### 例1: パーティションフィルタリングの効果がないクエリパターンの場合

前述の `/country=JP/year=2024/month=12/day=19/hour=10/data.parquet` のようなパーティション構造のテーブルに対して、
```
SELECT * FROM table_name WHERE device_type=ABC
```
のように**WHERE句がパーティション要素と無関係なクエリパターン**が多くを占める場合、パーティションインデックスを設定したとしても効果はかなり限定的となる上、コスト面のデメリットも発生してしまうため注意が必要です。

### 例2: パーティション構造の階層数が多く、各階層のフィルタリング頻度に偏りがない場合

(具体的な項目例を挙げるのが厳しかったので、雑な例になりました)

`column_a`, `column_b`, `column_c`, ..., `column_z` の26項目の要素からなるパーティション構造を想定します。 (パーティション例: `/column_a=0/column_b=1/column_c=2/.../column_z=1/`)
対するクエリパターンとしてはWHERE句で `column_a` から `column_z` まで一度に1-3個ずつ、頻度の偏りなく指定されることを想定します。
この場合、**パーティションインデックスが最大で3つしか設定できない仕様 (且つ、上限緩和不可)** があるため、3つのインデックスをどのように設計してもカバーできないクエリパターンが多数残ってしまいます。


# 3. Athena Partition Projectionは検討したか

Athena をクエリエンジンとした場合限定ですが、パーティションインデックスと同じく、パーティションフィルタリングによってパフォーマンスを効率化するアプローチが Partition Projection です。
パーティションインデックスの利用には前提条件として対象データを Glue でカタログ管理 (テーブル化) する必要があり、検討時間も含めて多くの手間とコストがかかります。 (コストに関しては次節で説明)
そのため、以下条件のいずれかに該当する場合はまず Partition Projection の採用を検討し、将来的にパーティションインデックスが必要になってから導入することをお勧めします。

- 当面の間、クエリエンジンは Athena のみ利用を想定している場合
- 恒常的なユースケースではなく、短期集中的にデータを分析したい場合


# 4. パーティションインデックスによるランニングコスト増分に値するパフォーマンス改善効果が見込めるか

(2024年12月19日時点で [(公式) AWS Glue pricing](https://aws.amazon.com/jp/glue/pricing/) の東京リージョンの料金を記載します)

パーティションインデックスを設定したことにより以下ランニングコストが発生します。

- 1つのインデックスにつき、設定対象テーブルのパーティション数分のData Catalog Metadata Pricingが発生

```text:パーティション数100万のテーブルにインデックスを3つ設定した場合のコスト例
1,000,000 partitions * 3 indexes * $1.00 per 100,000 objects per month = $30 per month
```

インデックスは多く設定してもパフォーマンス上のデメリットはないとのこと (実は怪しい面もあり、末尾の余談で言及する) ですが、上の例のように1つのインデックスにつき $10 per month 発生するのが勿体無いと考えた場合は必要最低限のインデックスを設定しましょう。


# まとめ

パーティションインデックスを有効に利用する上で確認しておきたいポイントについてまとめました。
あまり細かく説明できていないところも多いのですが、本記事をきっかけにより詳しく調べる機会につながれば嬉しいです。
疑問やおかしい点などお気づきがあれば是非コメント欄に書いていただけると助かります。
最後まで読んでいただきありがとうございました。


### 余談

(以下は2022年に前職にて担当いただいたAWS SAの方に確認した内容であり、投稿時点で状況が異なっている可能性があるため軽く読み流してください)

複数のインデックスを設定した場合、クエリ時にどのパーティションを指定する必要はありません。

...というか**こちらで指定することができません**。
独自のアルゴリズムで最適なインデックスを適用してくれるそうですが、最終的にどのインデックスが適用されたのかもクエリのレスポンスやログに一切残らないので完全にブラックボックスです。
個別事例なので詳細は伏せますが、複数のインデックスを設定した上で様々なクエリパターンのパフォーマンスを検証したところ、一部のクエリパターンにて明らかに最適なものとは別のインデックスが選択された傾向に見える結果となりました。

何が言いたいのかというと、パーティションインデックスを導入しても理論通り効果が必ずしも保証されないことを念頭に置いておくことをお勧めします。