

# Athena のパフォーマンスを最適化する
<a name="performance-tuning"></a>

このトピックでは、Athena クエリのパフォーマンスを向上させるための一般的な情報と具体的な提案、および制限やリソース使用量に関連するエラーの回避方法について説明します。

大まかには、最適化は、サービス、クエリ、データ構造のカテゴリにグループ化できます。サービスレベルで行われた決定、クエリの記述方法に関する決定、データとテーブルの構造に関する決定はすべてパフォーマンスに影響を及ぼす可能性があります。

**Topics**
+ [サービスの利用を最適化する](performance-tuning-service-level-considerations.md)
+ [クエリを最適化する](performance-tuning-query-optimization-techniques.md)
+ [データを最適化する](performance-tuning-data-optimization-techniques.md)
+ [列指向ストレージ形式を使用する](columnar-storage.md)
+ [パーティショニングとバケット化を使用する](ctas-partitioning-and-bucketing.md)
+ [データのパーティション化](partitions.md)
+ [Amazon Athena でパーティション射影を使用する](partition-projection.md)
+ [Amazon S3 のスロットリングを防ぐ](performance-tuning-s3-throttling.md)
+ [追加リソース](performance-tuning-additional-resources.md)

# サービスの利用を最適化する
<a name="performance-tuning-service-level-considerations"></a>

サービスレベルに関する考慮事項には、アカウントごとに実行するワークロードの数、Athena だけでなくサービス全体でのサービスクォータ、「リソース不足」エラーを減らす方法の検討が含まれます。

**Topics**
+ [同じアカウント内で複数のワークロードを運用する](#performance-tuning-service-quotas)
+ [「リソース不足」エラーを減らす](#performance-tuning-resource-limits)

## 同じアカウント内で複数のワークロードを運用する
<a name="performance-tuning-service-quotas"></a>

Athena はクォータを使用して、クエリの同時実行率と API リクエスト率をアカウントレベルで制限します。これらのクォータを超えると、実行中または送信時にクエリが失敗する可能性があります。これらのクォータの詳細については、「[サービスクォータ](service-limits.md)」を参照してください。

同じ AWS アカウント内で複数のワークロードを実行している場合、ワークロードは同じアカウントレベルのクォータで競合します。例えば、1 つのワークロードで予期しないクエリのバーストが発生した場合、同じアカウントで実行されている別のワークロードでキュー時間が長くなったり、最悪のケースではスロットリングによってクエリ送信が失敗したりする可能性があります。

CloudWatch を使用して、グラフやダッシュボードを使ってサービスの使用状況をモニタリングすることをお勧めします。また、同時クエリのサービスクォータに使用量が近づいたときに警告する CloudWatch アラームを設定して、クォータ制限に達する前にアクションを実行することもできます。詳細については、「[CloudWatch による Athena 使用状況メトリクスのモニタリング](monitoring-athena-usage-metrics.md)」を参照してください。

クエリの同時実行を制御し、アカウント内のワークロードを分離するには、キャパシティ予約を使用します。キャパシティ予約は、単一のアカウント内で専用のクエリ処理キャパシティを提供します。キャパシティはデータ処理ユニット (DPU) で測定され、それぞれ追加または削除してクエリの同時実行数を増減できます。キャパシティ予約を使用すると、1 つ以上のワークグループにキャパシティを割り当てることで、アカウント内のワークロードを相互に分離できます。詳細については、「[クエリ処理キャパシティを管理する](capacity-management.md)」を参照してください。

関連しないワークロードは異なる AWS アカウントに分離すべきですが (開発環境と本番環境を分離するなど)、この方法ではクエリの同時実行性をスケーラブルに向上させることはできません。代わりに、キャパシティ予約を使用して、1 つのアカウント内でクエリ処理の管理とスケーリングが可能になります。

### 他のサービスのクォータを検討する
<a name="performance-tuning-quotas-in-other-services"></a>

Athena がクエリを実行すると、クォータを適用する他のサービスを呼び出せます。クエリの実行中に、Athena は AWS Glue Data Catalog、Amazon S3、および IAM や AWS KMS などその他の AWS サービスなどに API 呼び出しを行えます。[フェデレーションクエリ](federated-queries.md)を使用する場合、Athena は AWS Lambda に呼び出しも行います。これらのサービスにはすべて、超過できる独自の制限と割り当てがあります。クエリ実行でこれらのサービスからエラーが発生すると、ソースサービスからのエラーも含めて失敗します。復元可能なエラーは再試行されますが、問題が時間内に解決されない場合、クエリが失敗する可能性があります。エラーメッセージをよく読んで、Athena からのものか別のサービスからのものかを判断してください。関連するエラーの一部については、このパフォーマンスチューニングに関するセクションで説明します。

Amazon S3 サービスクォータに起因するエラーの回避方法の詳細については、このドキュメントの後半の「[ファイルが多すぎにならないようにする](performance-tuning-data-optimization-techniques.md#performance-tuning-avoid-having-too-many-files)」を参照してください。Amazon S3 のパフォーマンスの最適化の詳細については、「*Amazon S3 ユーザーガイド*」の「[ベストプラクティスの設計パターン: Amazon S3 パフォーマンスの最適化](https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance.html)」を参照してください。

## 「リソース不足」エラーを減らす
<a name="performance-tuning-resource-limits"></a>

Athena は分散クエリエンジンでクエリを実行します。クエリを送信すると、Athena エンジンのクエリプランナーはクエリを実行するのに必要な計算能力を見積もり、それに応じて計算ノードのクラスターを準備します。DDL クエリなどの一部のクエリは、1 つのノードでのみ実行されます。大規模なデータセットに対する複雑なクエリでは、はるかに大きなクラスターで実行されます。ノードは均一であり、メモリ、CPU、およびディスク構成は同じです。Athena は、より要求の厳しいクエリを処理するためにスケールアップするのではなく、スケールアウトします。

クエリの要求は、クエリを実行するクラスターで使用可能なリソースを超えることがあります。この場合にはクエリは失敗し、「このスケールファクターでリソースを使い果たしました」というエラーが表示されます。

最も一般的に消費されるリソースはメモリですが、まれにディスク容量になる場合もあります。メモリエラーは通常、エンジンが結合またはウィンドウ関数を実行するときに発生しますが、カウントや集計が異なる場合もあります。

クエリが「リソース不足」エラーで一度失敗した場合でも、もう一度実行すると成功する可能性があります。クエリの実行は確定的ではありません。データの読み込みにかかる時間や中間データセットがノードにどのように分散されるかなどの要因により、リソースの使用量が異なる場合があります。たとえば、2 つのテーブルを結合するクエリで、結合条件の値の分布に大きな偏りがあるとします。このようなクエリはほぼ成功しますが、最も一般的な値が同じノードで処理されてしまうと失敗することがあります。

クエリが利用可能なリソースを超えないようにするには、このドキュメントに記載されているパフォーマンスチューニングのヒントを参考にしてください。特に、使用可能なリソースを使い果たすクエリを最適化する方法のヒントについては、「[結合を最適化する](performance-tuning-query-optimization-techniques.md#performance-tuning-optimizing-joins)」、「[ウィンドウ関数の範囲を縮小したり、削除したりする](performance-tuning-query-optimization-techniques.md#performance-tuning-optimizing-window-functions)」、および「[近似値を使用してクエリを最適化する](performance-tuning-query-optimization-techniques.md#performance-tuning-optimizing-queries-by-using-approximations)」を参照してください 

# クエリを最適化する
<a name="performance-tuning-query-optimization-techniques"></a>

このセクションで説明するクエリ最適化手法を使用して、クエリの実行を高速化したり、Athena のリソース制限を超えるクエリの回避策として使用します。

## 結合を最適化する
<a name="performance-tuning-optimizing-joins"></a>

分散クエリエンジンで結合を実行するには、さまざまな方法があります。最も一般的なのは、分散ハッシュ結合と複雑な結合条件のクエリの 2 つです。

### 分散ハッシュ結合では、大きなテーブルを左側に配置し、小さなテーブルを右側に配置します
<a name="performance-tuning-distributed-hash-join"></a>

最も一般的なタイプの結合では、結合条件として等価比較を使用します。Athena はこのタイプの結合を分散ハッシュ結合として実行します。

分散型ハッシュ結合では、エンジンは結合の片側からルックアップテーブル (ハッシュテーブル) を作成します。この側は*ビルド側*と呼ばれます。ビルド側のレコードはノード全体に分散されます。各ノードは、そのサブセットのルックアップテーブルを作成します。結合のもう一方の側 (*プローブ側*と呼ばれる) がノードを介してストリーミングされます。プローブ側のレコードは、ビルド側と同じ方法でノードに分散されます。これにより、各ノードは独自の検索テーブルで一致するレコードを検索して結合を実行できます。

結合のビルド側から作成されたルックアップテーブルがメモリに収まらない場合、クエリが失敗する可能性があります。ビルド側の合計サイズが利用可能なメモリよりも小さい場合でも、レコードの分布に大きな偏りがあると、クエリが失敗する可能性があります。極端なケースでは、すべてのレコードの結合条件の値が同じで、1 つのノードのメモリに収まる必要がある場合があります。スキューの少ないクエリでも、値のセットが同じノードに送信され、その値の合計が使用可能なメモリを超えると、失敗する可能性があります。ノードにはレコードをディスクにスピルする機能がありますが、スピルするとクエリの実行が遅くなり、クエリの失敗を防ぐには不十分である場合があります。

Athena は、大きい方のリレーションをプローブ側に、小さい方のリレーションをビルド側として使用するように結合の順序を変更しようとします。ただし、Athena はテーブル内のデータを管理しないため、情報が限られており、多くの場合、最初のテーブルが大きく、2 番目のテーブルが小さいと想定する必要があります。

等価ベースの結合条件で結合を記述する場合、`JOIN` キーワードの左側の表がプローブ側、右側の表がビルド側であると仮定します。正しいテーブル (ビルド側) がテーブルのうち小さい方であることを確認してください。結合のビルド側をメモリに収まるほど小さくできない場合は、ビルドテーブルのサブセットを結合する複数のクエリを実行することを検討してください。

### EXPLAIN を使用して複雑な結合を持つクエリを分析する
<a name="performance-tuning-other-join-types"></a>

結合条件が複雑なクエリ (`LIKE`、`>`、または他の演算子を使用するクエリなど) では、多くの場合、計算負荷が高くなります。最悪の場合には、結合の片側のすべてのレコードを、結合の反対側のすべてのレコードと比較する必要があります。実行時間はレコード数の 2 乗に比例して長くなるため、このようなクエリには最大実行時間を超えるリスクがあります。

Athena がクエリをどのように実行するかを事前に確認するには、`EXPLAIN` ステートメントを使用できます。詳細については、「[Athena での EXPLAIN および EXPLAIN ANALYZE の使用](athena-explain-statement.md)」および「[Athena EXPLAIN ステートメントの結果を理解する](athena-explain-statement-understanding.md)」を参照してください。

## ウィンドウ関数の範囲を縮小したり、削除したりする
<a name="performance-tuning-optimizing-window-functions"></a>

ウィンドウ関数はリソースを大量に消費する操作であるため、クエリの実行が遅くなったり、「このスケールファクターではリソースを使い果たしました」というメッセージが表示され、クエリが失敗したりする可能性があります。ウィンドウ関数は、結果を計算するために操作したすべてのレコードをメモリに保持します。ウィンドウが非常に大きい場合、ウィンドウ関数のメモリが不足する可能性があります。

クエリが使用可能なメモリ制限内で実行されるように、ウィンドウ関数が処理するウィンドウのサイズを小さくしてください。そのようにするためには、`PARTITIONED BY` 句を追加するか、既存の分割節の範囲を絞り込めます。

### ウィンドウ以外の関数を使用する
<a name="performance-tuning-optimizing-window-functions-rewrite"></a>

ウィンドウ関数を含むクエリは、ウィンドウ関数なしで書き直せる場合があります。たとえば、`row_number` を使用して上位 `N` レコードを検索する代わりに、`ORDER BY` および `LIMIT` を使用できます。`row_number` または `rank` を使用してレコードの重複排除を行う代わりに、[max\$1by](https://trino.io/docs/current/functions/aggregate.html#max_by)、[min\$1by](https://trino.io/docs/current/functions/aggregate.html#min_by)、[任意](https://trino.io/docs/current/functions/aggregate.html#arbitrary) などの集計関数を使用できます。

たとえば、センサーからの更新を含むデータセットがあるとします。センサーは定期的にバッテリーの状態を報告し、位置情報などのメタデータを含みます。各センサーの最新のバッテリー状態とその位置を知りたい場合は、次のクエリを使用できます。

```
SELECT sensor_id,
       arbitrary(location) AS location,
       max_by(battery_status, updated_at) AS battery_status
FROM sensor_readings
GROUP BY sensor_id
```

位置などのメタデータはすべてのレコードで同じなので、`arbitrary` 関数を使用してグループから任意の値を選択できます。

`max_by` 関数を使用すると、最新のバッテリー状態を取得できます。`max_by` 関数は、別の列の最大値が見つかったレコードから列の値を選択します。この場合、グループ内の最終更新時刻を含むレコードのバッテリーステータスを返します。このクエリは、ウィンドウ関数を使用する同等のクエリよりも実行速度が速く、メモリ使用量も少なくて済みます。

## 集計を最適化する
<a name="performance-tuning-optimizing-aggregations"></a>

Athena が集約を実行すると、`GROUP BY` 句内の列を使用してレコードがワーカーノード全体に分散されます。レコードをグループと照合するタスクを可能な限り効率的に行うために、ノードはレコードをメモリに保持し、必要に応じてディスクに書き出そうとします。

`GROUP BY` 句に重複する列を含めないようにするのも良い考えです。列の数が少ないと必要なメモリも少なくなるため、使用する列の数が少ないグループを記述するクエリの方が効率的です。また、数値列は文字列よりもメモリ使用量が少なくなります。たとえば、数値のカテゴリ ID とカテゴリ名の両方を持つデータセットを集約する場合、`GROUP BY` 句にはカテゴリ ID 列のみを使用してください。

場合によっては、列が `GROUP BY` 句または集計式の一部である必要があるという事実を回避するために、クエリの `GROUP BY` 句に列が含まれることがあります。このルールに従わない場合、次のようなエラーメッセージが表示されることがあります。

 EXPRESSION\$1NOT\$1AGGREGATE: 1:8 行目:「カテゴリ」は集合式であるか、GROUP BY 句に含まれている必要があります 

`GROUP BY` 句に余分な列を追加しなくても済むように、次の例のように[任意](https://trino.io/docs/current/functions/aggregate.html#arbitrary)の関数を使用できます。

```
SELECT country_id,
       arbitrary(country_name) AS country_name,
       COUNT(*) AS city_count
FROM world_cities
GROUP BY country_id
```

`ARBITRARY` 関数はグループから任意の値を返します。この関数は、グループ内のすべてのレコードが 1 つの列に対して同じ値を持つことは把握しているがその値がグループを識別できない場合に便利です。

## 上位 N 個のクエリを最適化する
<a name="performance-tuning-optimizing-top-n-queries"></a>

`ORDER BY` 句は、クエリの結果をソートされた順序で返します。Athena は分散ソートを使用して、ソート操作を複数のノードで並行して実行します。

結果を厳密にソートする必要がない場合は、`ORDER BY` 句を追加しないでください。また、必ずしも必要ではない場合は、内部クエリへの `ORDER BY` の追加は避けてください。多くの場合、クエリプランナーは冗長なソートを削除できますが、これは保証されていません。このルールの例外は、内部クエリが上位 `N` の操作 (最新の `N` の値、または最も一般的な `N`の値を検索するなど) を実行している場合です。

Athena が `ORDER BY` を `LIMIT` を使用して見た場合、実行されている `N` クエリが上位のクエリであることを認識し、それに応じて専用の操作を行います。

**注記**  
Athena では、上位の `N` を使用する `row_number` のようなウィンドウ関数も多くの場合検出できますが、`ORDER BY` と `LIMIT` を使用するより単純なバージョンをお勧めします。詳細については、「[ウィンドウ関数の範囲を縮小したり、削除したりする](#performance-tuning-optimizing-window-functions)」を参照してください。

## 必要な列のみを含める
<a name="performance-tuning-include-only-required-columns"></a>

列が必ずしも必要ではない場合は、クエリに含めないでください。クエリが処理しなければならないデータが少ないほど、実行速度は速くなります。これにより、必要なメモリ量と、ノード間で送信する必要のあるデータ量の両方が削減されます。列形式のファイル形式を使用している場合、列の数を減らすと、Amazon S3 から読み取られるデータの量も減ります。

Athena には結果の列数に特別な制限はありませんが、クエリの実行方法によって、可能な列の合計サイズが制限されます。列を合わせたサイズには、名前とタイプが含まれます。

たとえば、次のエラーは、リレーションがリレーション記述子のサイズ制限を超えるために発生します。

 GENERIC\$1INTERNAL\$1ERROR: io.airlift.bytecode.CompilationException 

この問題を回避するには、クエリ内の列の数を減らす、またはサブクエリを作成して、より少ない量のデータを取得する `JOIN` を使用します。一番外側のクエリで `SELECT *` を実行するクエリがある場合は、`*` を必要な列のみのリストに変更する必要があります。

## 近似値を使用してクエリを最適化する
<a name="performance-tuning-optimizing-queries-by-using-approximations"></a>

Athena では、[近似集計関数](https://trino.io/docs/current/functions/aggregate.html#appro)をサポートしており、個別値、最も頻度の高い値、パーセンタイル (近似中央値を含む) のカウント、およびヒストグラムの作成を行えます。これらの関数は、正確な値が不要な場合に使用してください。

`COUNT(DISTINCT col)` 操作とは異なり、[approx\$1distinct](https://trino.io/docs/current/functions/aggregate.html#approx_distinct) はメモリ使用量がはるかに少なく、実行速度も速くなります。同様に、ヒストグラムの代わりに [numeric\$1histogram](https://trino.io/docs/current/functions/aggregate.html#numeric_histogram) を使用すると、[近似法](https://trino.io/docs/current/functions/aggregate.html#histogram)を使用するため、メモリ使用量が少なくなります。

## LIKE を最適化する
<a name="performance-tuning-optimizing-like"></a>

`LIKE` を使用して一致する文字列を検索できますが、文字列が長い場合は計算量が多くなります。[regexp\$1like](https://trino.io/docs/current/functions/regexp.html#regexp_like) 関数は、ほとんどの場合、より高速な代替手段であり、柔軟性にも優れています。

多くの場合、探している部分的な文字列を固定することで検索を最適化できます。たとえば、プレフィックスを探している場合は、'*% substr*%' の代わりに '*substr* %' を使用する方がはるかに優れています。または `regexp_like`、「^ *substr*」を使用している場合。

## UNION の代わりに UNION ALL を使用する
<a name="performance-tuning-use-union-all-instead-of-union"></a>

 `UNION ALL` と `UNION` は、2 つのクエリの結果を 1 つの結果にまとめる 2 つの方法です。 `UNION ALL` は最初のクエリのレコードを 2 番目のクエリと連結し、`UNION` は同じ処理を実行しますが、重複も削除します。`UNION` ではすべてのレコードを処理して重複を見つける必要があり、メモリと計算を大量に消費しますが、`UNION ALL` は比較的高速な操作です。レコードの重複排除が必要でない限り、`UNION ALL` はベストパフォーマンスを実現するために使用してください。

## 大きな結果セットには UNLOAD を使用する
<a name="performance-tuning-use-unload-for-large-result-sets"></a>

クエリの結果が大きくなることが予想される場合 (たとえば、数万行以上など)、UNLOAD を使用して結果をエクスポートします。ほとんどの場合、これは通常のクエリを実行するよりも高速です。また、`UNLOAD` を使用すると、出力をより細かく制御できます。

クエリの実行が終了すると、Athena は結果を 1 つの非圧縮 CSV ファイルとして Amazon S3 に保存します。結果が圧縮されないだけでなく、操作を並列化できないため、`UNLOAD` よりも時間がかかります。これとは対照的に、`UNLOAD` は結果をワーカーノードから直接書き込み、計算クラスターの並列処理を最大限に活用します。さらに、結果を圧縮形式や JSON や Parquet などの他のファイル形式で書き込むように `UNLOAD` を構成できます。

詳細については、「[UNLOAD](unload.md)」を参照してください。

## CTAS または Glue ETL を使用して、頻繁に使用する集計をマテリアライズする
<a name="performance-tuning-use-ctas-or-glue-etl-to-materialize-frequently-used-aggregations"></a>

クエリの「マテリアライズ」とは、事前に計算された複雑なクエリ結果 (集計や結合など) を保存して後続のクエリで再利用することで、クエリのパフォーマンスを向上させる方法です。

クエリの多くに同じ結合や集計が含まれている場合は、共通のサブクエリを新しいテーブルとして作成し、そのテーブルに対してクエリを実行できます。[クエリ結果からテーブルを作成する (CTAS)](ctas.md)、または [Glue ETL](https://aws.amazon.com/glue) などの専用 ETL ツールを使用して新しいテーブルを作成できます。

たとえば、オーダーデータセットのさまざまな側面を表示するウィジェットを含むダッシュボードがあるとします。各ウィジェットには独自のクエリがありますが、クエリはすべて同じ結合とフィルターを共有します。注文テーブルは品目テーブルと結合され、過去 3 か月のみ表示するフィルターがあります。これらのクエリに共通する機能がわかれば、ウィジェットが使用できる新しいテーブルを作成できます。これにより、重複が減り、パフォーマンスが向上します。欠点は、新しいテーブルを最新の状態に保つ必要があることです。

## クエリ結果の再利用
<a name="performance-tuning-reuse-query-results"></a>

同じクエリが短期間に複数回実行されることはよくあります。たとえば、複数のユーザーが同じデータダッシュボードを開いたときに発生する可能性があります。クエリを実行するときに、以前に計算した結果を再利用するよう Athena に指示できます。再利用できる結果の最大保管期間を指定します。同じクエリがその時間枠内で以前に実行された場合、Athena はクエリを再実行する代わりにそれらの結果を返します。詳細については、「*Amazon Athena ユーザーガイド*」の [Athena でクエリ結果を再利用する](reusing-query-results.md)、「*AWS ビッグデータブログ*」の「[Amazon Athena クエリ結果の再利用によるコスト削減とクエリパフォーマンスの向上](https://aws.amazon.com/blogs/big-data/reduce-cost-and-improve-query-performance-with-amazon-athena-query-result-reuse/)」を参照してください。

# データを最適化する
<a name="performance-tuning-data-optimization-techniques"></a>

パフォーマンスはクエリだけでなく、データセットの構成方法や使用するファイル形式と圧縮にも大きく依存します。

## データのパーティション化
<a name="performance-tuning-partition-your-data"></a>

パーティショニングを行うと、テーブルが複数部分に分割され、日付、国、地域などのプロパティに基づいて関連データがまとめられます。パーティションキーは仮想列として機能します。パーティションキーはテーブルの作成時に定義し、クエリのフィルタリングに使用します。パーティションキー列をフィルタリングすると、一致するパーティションのデータのみが読み取られます。たとえば、データセットが日付で分割されていて、クエリに先週のみ一致するフィルターが設定されている場合、先週のデータのみが読み取られます。パーティショニングについての詳細は、「[データのパーティション化](partitions.md)」を参照してください。

## クエリをサポートするパーティションキーを選択する
<a name="performance-tuning-pick-partition-keys-that-will-support-your-queries"></a>

パーティショニングはクエリのパフォーマンスに大きな影響を与えるため、データセットとテーブルを設計するときは、パーティションの分割方法を慎重に検討してください。パーティションキーが多すぎると、データセットが断片化し、ファイルが多すぎたり、ファイルが小さすぎたりする可能性があります。逆に、パーティションキーが少なすぎたり、パーティショニングがまったく行われなかったりすると、クエリが必要以上に多くのデータをスキャンすることになります。

### まれなクエリの最適化は避ける
<a name="performance-tuning-avoid-optimizing-for-rare-queries"></a>

最もよく使われるクエリを最適化し、まれであるクエリには最適化しないようにするのが良い戦略です。たとえば、クエリが日単位の期間を対象としている場合は、一部のクエリがそのレベルに絞り込まれていても、時間単位で分割しないでください。データに詳細なタイムスタンプ列がある場合、時間別にフィルタリングするまれなクエリではタイムスタンプ列を使用できます。まれなケースで必要以上のデータをスキャンしたとしても、まれなケースという理由で全体的なパフォーマンスを低下させることは、通常は良いトレードオフとは言えません。

クエリでスキャンするデータ量を減らしてパフォーマンスを向上させるには、列指向のファイル形式を使用し、レコードをソートしたままにしてください。時間ごとに分割する代わりに、レコードをタイムスタンプでソートしておきます。短い時間枠でのクエリでは、タイムスタンプによるソートは、時間ごとの分割とほぼ同じくらい効率的です。さらに、通常はタイムスタンプでソートしても、日単位でカウントされたタイムウィンドウでのクエリのパフォーマンスが低下することはありません。詳細については、「[列指向ファイルフォーマットを使用する](#performance-tuning-use-columnar-file-formats)」を参照してください。

数万のパーティションを含むテーブルでのクエリは、すべてのパーティションキーに述語がある方がパフォーマンスが向上することに注意してください。これが、最も一般的なクエリ用にパーティションスキームを設計するもう一つの理由です。詳細については、「[等式によるパーティションのクエリ](#performance-tuning-query-partitions-by-equality)」を参照してください。

## パーティション射影を使用する
<a name="performance-tuning-use-partition-projection"></a>

パーティション射影は Athena の機能の 1 つであり、パーティション情報を AWS Glue Data Catalog には格納せず、AWS Glue のテーブルのプロパティにルールとして格納します。Athena は、パーティション射影が設定されたテーブルに対してクエリを計画する際、テーブルのパーティション射影ルールを読み取ります。Athena は、AWS Glue Data Catalog でパーティションを検索する代わりに、クエリとルールに基づいてメモリに読み込むパーティションを計算します。

パーティション射影は、パーティション管理を簡素化するだけでなく、多数のパーティションを持つデータセットのパフォーマンスを向上させることができます。クエリにパーティションキーの特定の値の代わりに範囲が含まれている場合、カタログで一致するパーティションを検索すると、パーティションの数が多いほど時間がかかります。パーティション射影を使用すると、カタログにアクセスせずにフィルターをメモリ内で計算でき、はるかに高速になります。

特定の状況では、パーティション射影によってパフォーマンスが低下する可能性があります。一例として、テーブルが「スパースな」場合が挙げられます。スパーステーブルには、パーティション射影の設定で記述されたパーティションキー値のすべての順列に関するデータはありません。スパーステーブルでは、クエリから計算されたパーティションセットとパーティションプロジェクション設定は、データがない場合でもすべて Amazon S3 に一覧表示されます。

パーティション射影を使用する場合は、必ずすべてのパーティションキーに述語を含めてください。Amazon S3 の一覧表示が不要にならないように、指定できる値の範囲を絞り込んでください。100 万の値の範囲を持つパーティションキーと、そのパーティションキーにフィルターがないクエリを想像してください。クエリを実行するには、Athena は少なくとも 100 万回の Amazon S3 リストオペレーションを実行する必要があります。パーティション射影を使用するか、カタログにパーティション情報を保管するかに関係なく、特定の値に対してクエリを実行すると、クエリは最も速くなります。詳細については、「[等式によるパーティションのクエリ](#performance-tuning-query-partitions-by-equality)」を参照してください。

パーティション射影用のテーブルを設定するときは、指定する範囲が適切であることを確認してください。クエリにパーティションキーの述語が含まれていない場合は、そのキーの範囲内のすべての値が使用されます。データセットが特定の日付に作成された場合は、その日付を任意の日付範囲の開始点として使用します。終了日の範囲として `NOW` を使用します。値の数が多い数値範囲は避け、代わりに[注入された](partition-projection-dynamic-id-partitioning.md#partition-projection-injection)型の使用を検討してください。

パーティション射影の詳細については、「[Amazon Athena でパーティション射影を使用する](partition-projection.md)」を参照してください。

## パーティションインデックスの使用
<a name="performance-tuning-use-partition-indexes"></a>

パーティションインデックスは、多数のパーティションを持つテーブルのパーティションルックアップのパフォーマンスを向上させる AWS Glue Data Catalog の機能です。

カタログ内のパーティションのリストは、リレーショナルデータベースのテーブルのようなものです。このテーブルには、パーティションキー用の列と、パーティションの場所用の別の列があります。分割テーブルをクエリすると、このテーブルをスキャンしてパーティションの場所が検索されます。

リレーショナルデータベースと同様に、インデックスを追加することでクエリのパフォーマンスを向上できます。複数のインデックスを追加して、さまざまなクエリパターンをサポートできます。AWS Glue Data Catalog パーティションインデックスは、`>`、`>=` などの等価演算子と比較演算子の両方をサポートし、`<` は `AND` 演算子を結合します。詳細については、「*AWS Glue 開発者ガイド*」の「[AWS Glue でのパーティションインデックスの使用](https://docs.aws.amazon.com/glue/latest/dg/partition-indexes.html)」と、「*AWS Big Data Blog*」の「[Improve Amazon Athena query performance using AWS Glue Data Catalog partition indexes](https://aws.amazon.com/blogs/big-data/improve-amazon-athena-query-performance-using-aws-glue-data-catalog-partition-indexes/)」を参照してください。

## パーティションキーのタイプとして常に STRING を使用する
<a name="performance-tuning-always-use-string-as-the-type-for-partition-keys"></a>

パーティションキーをクエリする場合、Athena ではパーティションフィルタリングを AWS Glue にプッシュダウンするためにパーティションキーのタイプ `STRING` が必要であることを覚えておいてください。パーティションの数が少なくない場合は、他のタイプを使用するとパフォーマンスが低下する可能性があります。パーティションキーの値が日付型または数値型の場合は、クエリ内の適切な型にキャストしてください。

## 古いパーティションや空のパーティションを削除する
<a name="performance-tuning-remove-old-and-empty-partitions"></a>

Amazon S3 のパーティションから (Amazon S3 [ライフサイクル](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html)を使用するなどして) データを削除する場合は、AWS Glue Data Catalog からそのパーティションエントリも削除する必要があります。クエリのプランニング中、クエリに一致するパーティションはすべて Amazon S3 に一覧表示されます。空のパーティションが多数ある場合、それらのパーティションを一覧表示することによるオーバーヘッドは悪影響をおよぼす可能性があります。

また、パーティションが何千もある場合は、不要になった古いデータのパーティションメタデータを削除することを検討してください。たとえば、クエリで 1 年以上前のデータが検索されない場合、古いパーティションのパーティションメタデータを定期的に削除できます。パーティションの数が数万に増えた場合、未使用のパーティションを削除すると、すべてのパーティションキーに述語が含まれていないクエリを高速化できます。クエリにすべてのパーティションキーに述語を含める方法については、「[等式によるパーティションのクエリ](#performance-tuning-query-partitions-by-equality)」を参照してください。

## 等式によるパーティションのクエリ
<a name="performance-tuning-query-partitions-by-equality"></a>

すべてのパーティションキーに等価述語を含むクエリでは、パーティションメタデータを直接読み込めるため、実行速度が速くなります。1 つ以上のパーティションキーに述語がない場合や、述語で値の範囲を選択するクエリは避けてください。このようなクエリでは、すべてのパーティションのリストをフィルタリングして、一致する値を見つける必要があります。ほとんどのテーブルではオーバーヘッドは最小限ですが、パーティションが数万以上あるテーブルではオーバーヘッドが大きくなる可能性があります。

クエリを書き直してパーティションを等値でフィルタリングできない場合は、パーティション射影を試してみてください。詳細については、「[パーティション射影を使用する](#performance-tuning-use-partition-projection)」を参照してください。

## パーティションのメンテナンスに MSCK REPAIR TABLE を使用しないでください
<a name="performance-tuning-avoid-using-msck-repair-table-for-partition-maintenance"></a>

`MSCK REPAIR TABLE` は実行に時間がかかる場合があり、新しいパーティションを追加するだけで、古いパーティションは削除されないため、パーティションを管理する効率的な方法ではありません (「[考慮事項と制限事項](msck-repair-table.md#msck-repair-table-considerations)」を参照)。

パーティションは、[AWS Glue Data Catalog API](https://docs.aws.amazon.com/glue/latest/dg/aws-glue-api-catalog.html)、[ALTER TABLE ADD PARTITION](alter-table-add-partition.md) または[AWS Glueクローラー](https://docs.aws.amazon.com/glue/latest/dg/crawler-running.html)を使用して手動で管理する方が適切です。別の方法として、パーティション射影を使用できます。これにより、パーティションを完全に管理する必要がなくなります。詳細については、「[Amazon Athena でパーティション射影を使用する](partition-projection.md)」を参照してください。

## クエリがパーティションスキームと互換性があることを確認してください。
<a name="performance-tuning-validate-that-your-queries-are-compatible-with-the-partitioning-scheme"></a>

クエリがどのパーティションをスキャンするかは、[`EXPLAIN`](athena-explain-statement.md) ステートメントを使用して事前に確認できます。クエリの前に `EXPLAIN` キーワードを付けてから、`EXPLAIN` 出力の下部にある各テーブルのソースフラグメント（たとえば、`Fragment 2 [SOURCE]`）を探します。右側がパーティションキーとして定義されている割り当てを探してください。下の行には、クエリの実行時にスキャンされるパーティションキーのすべての値のリストが含まれています。

たとえば、`dt` パーティションキーを含むテーブルに対してクエリを実行し、`EXPLAIN` のクエリのプレフィックスを付けたとします。クエリ内の値が日付で、フィルターで 3 日間の範囲を選択した場合、`EXPLAIN` 出力は次のようになります。

```
dt := dt:string:PARTITION_KEY
    :: [[2023-06-11], [2023-06-12], [2023-06-13]]
```

`EXPLAIN` 出力は、プランナーがクエリと一致するこのパーティションキーの値を 3 つ見つけたことを示しています。また、それらの値が何であるかも表示されます。`EXPLAIN` の使用の詳細については、「[Athena での EXPLAIN および EXPLAIN ANALYZE の使用](athena-explain-statement.md)」と「[Athena EXPLAIN ステートメントの結果を理解する](athena-explain-statement-understanding.md)」を参照してください。

## 列指向ファイルフォーマットを使用する
<a name="performance-tuning-use-columnar-file-formats"></a>

Parquet や ORC のような列指向のファイル形式は、分散分析ワークロード向けに設計されています。データを行別ではなく列別に整理します。データを列形式で整理することには、次のような利点があります。
+ クエリに必要な列のみが読み込まれる
+ ロードする必要のあるデータの総量が減る
+ 列の値はまとめて保存されるため、データを効率的に圧縮できる 
+ ファイルには、エンジンが不要なデータのロードをスキップできるようにするメタデータを含められる

ファイルメタデータの使用方法の例として、ファイルメタデータには、データページの最小値と最大値に関する情報を含められます。クエリされた値がメタデータに記載されている範囲にない場合は、ページをスキップできます。

このメタデータを使用してパフォーマンスを向上させる方法の 1 つは、ファイル内のデータを確実にソートすることです。たとえば、`created_at` エントリが短期間にあるレコードを検索するクエリがあるとします。`created_at` データが列でソートされている場合、Athena はファイルメタデータの最小値と最大値を使用して、データファイルの不要な部分をスキップできます。

列指向のファイル形式を使用する場合は、ファイルが小さすぎないことを確認してください。[ファイルが多すぎにならないようにする](#performance-tuning-avoid-having-too-many-files) で説明したように、小さなファイルが多いデータセットはパフォーマンスの問題を引き起こします。これは特に列指向ファイル形式の場合に当てはまります。小さなファイルの場合、列指向ファイル形式のオーバーヘッドの方がメリットよりも重要です。

Parquet と ORC は内部的には行グループ (Parquet) とストライプ (ORC) で整理されていることに注意してください。行グループのデフォルトサイズは 128 MB、デフォルトのストライプサイズは 64 MB です。列が多い場合は、行グループとストライプサイズを大きくしてパフォーマンスを向上できます。行グループまたはストライプサイズをデフォルト値より小さくすることはお勧めしません。

他のデータ形式を Parquet または ORC に変換するには、AWS Glue ETL または Athena を使用できます。詳細については、「[列形式に変換する](columnar-storage.md#convert-to-columnar)」を参照してください。

## データを圧縮する
<a name="performance-tuning-compress-data"></a>

Athena は幅広い圧縮形式をサポートしています。圧縮データのクエリは、解凍前にスキャンされたバイト数に応じて支払われるため、高速かつ安価です。

[gzip](https://www.gnu.org/software/gzip/) 形式は圧縮率が高く、他のツールやサービスを幅広くサポートしています。[zstd](https://facebook.github.io/zstd/) (Zstandard) 形式は、パフォーマンスと圧縮率のバランスが取れた新しい圧縮形式です。

JSON や CSV データなどのテキストファイルを圧縮するときは、ファイル数とファイルサイズのバランスをとるようにしてください。ほとんどの圧縮形式では、リーダーはファイルを最初から読み取る必要があります。つまり、圧縮されたテキストファイルは一般的に並行して処理できません。大きな非圧縮ファイルは、クエリ処理中の並列性を高めるためにワーカー間で分割されることがよくありますが、これはほとんどの圧縮形式では不可能です。

[ファイルが多すぎにならないようにする](#performance-tuning-avoid-having-too-many-files) で説明したように、ファイルが多すぎたり少なすぎたりしない方がよいでしょう。ファイル数によってクエリを処理できるワーカーの数が制限されるため、このルールは圧縮ファイルの場合に特に当てはまります。

Athena での圧縮の使用の詳細については、「[Athena で圧縮を使用する](compression-formats.md)」を参照してください。

## カーディナリティが高いキーの検索にはバケットを使用する
<a name="performance-tuning-use-bucketing-for-lookups-on-keys-with-high-cardinality"></a>

バケット処理とは、いずれかの列の値に基づいてレコードを個別のファイルに分散する手法です。これにより、同じ値を持つすべてのレコードが同じファイルにあることが保証されます。バケット化は、カーディナリティの高いキーがあり、クエリの多くがそのキーの特定の値を検索する場合に役立ちます。

たとえば、特定のユーザーのレコードセットをクエリするとします。データがユーザー ID 別にまとめられている場合、Athena は特定の ID のレコードを含むファイルと含まないファイルを事前に把握します。これにより、Athena はその ID を含められるファイルのみを読み取れるため、読み取るデータ量を大幅に削減できます。また、特定の ID のデータを検索するために必要となる計算時間も短縮されます。

### クエリが列内の複数の値を頻繁に検索する場合は、バケット化を避ける
<a name="performance-tuning-disadvantages-of-bucketing"></a>

クエリでデータがバケット化されている列で複数の値を頻繁に検索する場合、バケット化の価値は低くなります。クエリする値が多いほど、すべて、またはほとんどのファイルを読み取らなければならない可能性が高くなります。たとえば、バケットが 3 つあり、クエリが 3 つの異なる値を検索する場合、すべてのファイルを読み取る必要がある場合があります。バケット処理は、クエリが単一の値を検索するときに最も効果的です。

詳細については、「[パーティショニングとバケット化を使用する](ctas-partitioning-and-bucketing.md)」を参照してください。

## ファイルが多すぎにならないようにする
<a name="performance-tuning-avoid-having-too-many-files"></a>

データセットが多数の小さなファイルで構成されていると、全体的なクエリパフォーマンスが低下します。Athena がクエリを計画すると、すべてのパーティションの場所が一覧表示されるため、時間がかかります。各ファイルの処理と要求には、計算上のオーバーヘッドもあります。したがって、Amazon S3 から 1 つの大きなファイルをロードする方が、多数の小さいファイルから同じレコードをロードするよりも高速です。

極端なケースでは、Amazon S3 のサービス制限が発生する可能性があります。Amazon S3 は、1 つのインデックスパーティションに対して 1 秒あたり最大 5,500 の要求をサポートします。最初は、バケットは単一のインデックスパーティションとして扱われますが、リクエストの負荷が増えると、複数のインデックスパーティションに分割できます。

Amazon S3 はリクエストパターンを調べ、キープレフィックスに基づいて分割します。データセットが何千ものファイルで構成されている場合、Athena からのリクエストはリクエストクォータを超える可能性があります。ファイルが少なくても、同じデータセットに対して複数のクエリを同時に実行すると、クォータを超える可能性があります。同じファイルにアクセスする他のアプリケーションが、リクエストの総数に影響する可能性があります。

リクエストレート `limit` を超えると、Amazon S3 は次のエラーを返します。このエラーは Athena のクエリのステータス情報に含まれています。

 SlowDown: リクエスト率を下げてください 

トラブルシューティングを行うには、まず、エラーの原因が単一のクエリなのか、同じファイルを読み取る複数のクエリなのかを判断します。後者の場合は、同時に実行されないようにクエリの実行を調整します。これを実現するには、アプリケーションにキューイングメカニズムを追加するか、リトライを行う必要があります。

1 つのクエリを実行してもエラーが発生する場合は、データファイルを組み合わせるか、クエリを変更して読み込むファイルの数を減らしてみてください。小さなファイルを結合する最適なタイミングは、ファイルが書き込まれる前です。そのためには、以下の方法を検討してください。
+ ファイルを書き込むプロセスを変更して、より大きなファイルを書き込んでください。たとえば、レコードが書き込まれる前にレコードを長時間バッファリングできます。
+ Amazon S3 上の場所にファイルを置き、Glue ETL などのツールを使用してファイルをより大きなファイルにまとめます。次に、サイズの大きいファイルをテーブルが指す場所に移動します。詳細については、「*AWS Glue デベロッパーガイド*」の「[大きなグループの入力ファイルの読み込み](https://docs.aws.amazon.com/glue/latest/dg/grouping-input-files.html)」か、および「*AWS re:Post ナレッジセンター*」の「[より大きなファイルを出力するように AWS Glue ETL ジョブを構成するには](https://repost.aws/knowledge-center/glue-job-output-large-files)」を参照してください。
+ パーティションキーの数を減らしてください。パーティションキーが多すぎると、各パーティションのレコード数が少なくなり、小さなファイルが多すぎることがあります。作成するパーティションの決定については、「[クエリをサポートするパーティションキーを選択する](#performance-tuning-pick-partition-keys-that-will-support-your-queries)」を参照してください。

## パーティションの外部にストレージ階層を追加することは避けてください
<a name="performance-tuning-avoid-additional-storage-hierarchies-beyond-the-partition"></a>

クエリプランニングのオーバーヘッドを回避するには、ファイルを各パーティションの場所にフラット構造で格納します。追加のディレクトリ階層は使用しないでください。

Athena がクエリを計画すると、クエリに一致するすべてのパーティション内のすべてのファイルが一覧表示されます。Amazon S3 自体にはディレクトリはありませんが、慣例として `/` スラッシュはディレクトリ区切り文字として解釈されます。Athena がパーティションの場所を一覧表示すると、見つかったすべてのディレクトリが再帰的に一覧表示されます。パーティション内のファイルを 1 つの階層にまとめると、リストが複数回表示されます。

すべてのファイルが直接パーティションの場所にある場合は、ほとんどの場合、リスト操作を 1 回実行するのみで済みます。ただし、Amazon S3 はリストオペレーションごとに 1000 個のオブジェクトしか返さないため、パーティションに 1000 を超えるファイルがある場合は、複数の連続リストオペレーションが必要です。また、1 つのパーティションに 1000 個を超えるファイルがあると、さらに深刻なパフォーマンスの問題が発生する可能性があります。詳細については、「[ファイルが多すぎにならないようにする](#performance-tuning-avoid-having-too-many-files)」を参照してください。

## SymlinkTextInputFormat は必要な場合にのみ使用する
<a name="performance-tuning-use-symlinktextinputformat-only-when-necessary"></a>

[https://athena.guide/articles/stitching-tables-with-symlinktextinputformat](https://athena.guide/articles/stitching-tables-with-symlinktextinputformat) テクニックを使用すると、テーブルのファイルがパーティションにきちんと整理されていない状況を回避できます。たとえば、シンボリックリンクは、すべてのファイルが同じプレフィックスにある場合や、スキーマの異なるファイルが同じ場所にある場合に役立ちます。

ただし、シンボリックリンクを使用すると、クエリ実行に間接的なレベルが追加されます。これらのレベルのインダイレクションは、全体的なパフォーマンスに影響します。シンボリックリンク ファイルを読み取り、ファイルが定義する場所をリストする必要があります。これにより、通常の Hive テーブルでは不要な複数のラウンドトリップが Amazon S3 に追加されます。結論として、ファイルの再編成などのより適切なオプションが利用できない場合にのみ `SymlinkTextInputFormat` を使用してください。

# 列指向ストレージ形式を使用する
<a name="columnar-storage"></a>

[Apache Parquet](https://parquet.apache.org) や [ORC](https://orc.apache.org/) は、データを高速に取得できるように最適化された、AWS 分析アプリケーションで使用されている、列指向ストレージ形式です。

列指向ストレージ形式には以下の特性があるため、Athena での使用に適しています。
+ 列のデータ型に合わせて選択された圧縮アルゴリズムによる列ごとの圧縮で、Amazon S3 のストレージ領域を節約し、ディスク容量とクエリの処理中における I/O を削減します。
+ Parquet および ORC での述語プッシュダウンにより、Athena クエリが必要なブロックのみを取得できるようになり、クエリパフォーマンスが向上します。Athena クエリがデータから特定の列値を取得すると、データブロック述語からの統計 (最大値や最小値など) を使用して、そのブロックを読み取るかスキップするかを判断します。
+ Parquet および ORC での*データの分割*により、Athena がデータの読み取りを複数のリーダーに分割して、クエリ処理時における並列化を向上させることが可能になります。

既存の raw データを他のストレージ形式から Parquet または ORC に変換するには、Athena で [CREATE TABLE AS SELECT (CTAS)](ctas.md) クエリを実行してデータストレージ形式を Parquet もしくは ORC として指定する、または AWS Glue クローラを使用することができます。

## Parquet または ORC のどちらかを選択する
<a name="columnar-storage-choosing"></a>

ORC（最適化された列指向）または Parquet のどちらを選択するかは、特定の使用要件によって異なります。

Apache Parquet は効率的なデータ圧縮とエンコーディングスキームを提供しており、複雑なクエリの実行や大量データの処理に最適です。Parquet は [Apache Arrow ](https://arrow.apache.org/) での使用に最適化されているため、Arrow 関連ツールを使用する場合に便利です。

ORC は Hive データを効率的に保存する方法を提供します。ORC ファイルは Parquet ファイルよりも小さいことが多く、ORC インデックスを使用するとクエリを高速化できます。さらに、ORC は構造体、マップ、リストなどの複雑な型をサポートしています。

 Parquet または ORC のいずれかを選択するには、以下の点を考慮してください。

**クエリのパフォーマンス** — Parquet はより幅広い種類のクエリをサポートしているため、複雑なクエリを実行する場合は、Parquet の方が適している場合があります。

**複雑なデータ型** — 複雑なデータ型を使用している場合は、ORC の方が幅広い複合データ型をサポートしているので、より適している場合があります。

**ファイルサイズ** — ディスク容量が懸念される場合、通常、ORC を使用するとファイルが小さくなり、ストレージコストを低減できます。

**圧縮** — Parquet と ORC はどちらも優れた圧縮機能を備えていますが、最適な形式は特定のユースケースによって異なります。

**進化** — Parquet と ORC はどちらもスキーマの進化をサポートしています。つまり、時間の経過とともに列を追加、削除、または変更できます。

Parquet と ORC はどちらもビッグデータアプリケーションに適していますが、選択する前にシナリオの要件を検討してください。データとクエリに対してベンチマークを実行して、どの形式がユースケースに適したパフォーマンスを発揮するかを確認することをお勧めします。

## 列形式に変換する
<a name="convert-to-columnar"></a>

JSON や CSV などのソースデータを列指向形式に簡単に変換するためのオプションには、[CREATE TABLE AS](ctas.md) クエリや、AWS Glue でのジョブの実行などがあります。
+ `CREATE TABLE AS` (CTAS) クエリを使用すると、データを Parquet または ORC に 1 ステップで変換できます。例については、「[CTAS クエリの例](ctas-examples.md)」ページの「[例: クエリ結果を異なる形式で書き込む](https://docs.aws.amazon.com/athena/latest/ug/ctas-examples.html#ctas-example-format)」を参照してください。
+ ETL に Athena を利用して CSV から Parquet にデータを変換する方法については、「[ETL およびデータ分析での CTAS および INSERT INTO を使用する](ctas-insert-into-etl.md)」を参照してください。
+ CSV データを Parquet に変換する AWS Glue ジョブの実行については、AWS Big Data ブログの記事「[AWS Glue と Amazon S3 を使用してデータレイクの基礎を構築する](https://aws.amazon.com/blogs/big-data/build-a-data-lake-foundation-with-aws-glue-and-amazon-s3/)」の「データを CSV 形式から Parquet 形式に変換する」を参照してください。AWS Glue では CSV データの ORC への変換、あるいは JSON データの Parquet または ORC への変換にも同じ手法を使用できます。

# パーティショニングとバケット化を使用する
<a name="ctas-partitioning-and-bucketing"></a>

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

**Topics**
+ [パーティション化とは](ctas-partitioning-and-bucketing-what-is-partitioning.md)
+ [バケット化とは](ctas-partitioning-and-bucketing-what-is-bucketing.md)
+ [その他のリソース](ctas-partitioning-and-bucketing-additional-resources.md)

# パーティション化とは
<a name="ctas-partitioning-and-bucketing-what-is-partitioning"></a>

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

## パーティション化する方法の決定
<a name="ctas-partitioning-and-bucketing-deciding-how-to-partition"></a>

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

## パーティショニングされたテーブルを作成する
<a name="ctas-partitioning-and-bucketing-creating-a-partitioned-table"></a>

データセットをパーティション化すると、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` タイプにする必要があります。詳細については、「[パーティションキーのデータ型として文字列を使用](data-types-timestamps.md#data-types-timestamps-partition-key-types)」を参照してください。

その他の `CREATE TABLE` および `CREATE TABLE AS` 構文の詳細については、「[CREATE TABLE](create-table.md)」と「[CTAS テーブルのプロパティ](create-table-as.md#ctas-table-properties)」を参照してください。

## パーティショニングされたテーブルをクエリする
<a name="ctas-partitioning-and-bucketing-querying-partitioned-tables"></a>

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

### 例
<a name="ctas-partitioning-and-bucketing-partitioned-table-example-queries"></a>

テーブルを `sales_date` と `product_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 テーブルのクエリの違いに関する詳細については、「[同じく時間分割されているタイムスタンプフィールドのクエリを作成する方法](data-types-timestamps.md#data-types-timestamps-how-to-write-queries-for-timestamp-fields-that-are-also-time-partitioned)」を参照してください。

# バケット化とは
<a name="ctas-partitioning-and-bucketing-what-is-bucketing"></a>

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

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

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

## バケット化のメリット
<a name="ctas-partitioning-and-bucketing-bucketing-benefits"></a>

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

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

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

## バケット化された列でのフィルタリングでサポートされるデータ型
<a name="ctas-partitioning-and-bucketing-data-types-supported-for-filtering-on-bucketed-columns"></a>

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

## Hive と Spark のサポート
<a name="ctas-partitioning-and-bucketing-hive-and-spark-support"></a>

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

**注記**  
Athena では、`CREATE TABLE AS SELECT` ([CTAS](ctas.md)) クエリ内のパーティション数は 100 個に制限されています。同様に、[INSERT INTO](insert-into.md) ステートメントを使用すると、宛先テーブルに最大 100 個のパーティションのみを追加できます。  
この制限を超えると、HIVE\$1TOO\$1MANY\$1OPEN\$1PARTITIONS: Exceeded limit of 100 open writers for partitions/buckets (HIVE\$1TOO\$1MANY\$1OPEN\$1PARTITIONS: パーティション/バケットのオープンライターの制限である 100 を超えました) エラーメッセージが表示されることがあります。これらの制限を回避するには、CTAS ステートメントと、それぞれ最大 100 個のパーティションを作成または挿入する一連の `INSERT INTO` ステートメントを使用できます。詳細については、「[CTAS および INSERT INTO を使用して 100 パーティションの制限を回避する](ctas-insert-into.md)」を参照してください。

## CREATE TABLE のバケット化に関する例
<a name="ctas-partitioning-and-bucketing-bucketing-create-table-example"></a>

既存のバケット化されたデータセット用のテーブルを作成するには、`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) のバケット化に関する例
<a name="ctas-partitioning-and-bucketing-bucketing-create-table-as-example"></a>

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

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

## クエリのバケット化に関する例
<a name="ctas-partitioning-and-bucketing-bucketing-query-example"></a>

次のクエリ例では、特定の顧客が 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 ファイルまでです。

# その他のリソース
<a name="ctas-partitioning-and-bucketing-additional-resources"></a>
+ バケット化されたテーブルとパーティション化されたテーブルの両方を作成する `CREATE TABLE AS` の例については、「[例: バケット化およびパーティションされたテーブルを作成する](https://docs.aws.amazon.com/athena/latest/ug/ctas-examples.html#ctas-example-bucketed)」を参照してください。
+ Athena CTAS ステートメント、AWS Glue for Apache Spark の使用、Apache Iceberg テーブル用バケット化など、AWS データレイクへのバケット化の実装については、AWS Big Data Blog 記事「[Optimize data layout by bucketing with Amazon Athena and AWS Glue to accelerate downstream queries](https://aws.amazon.com/blogs/big-data/optimize-data-layout-by-bucketing-with-amazon-athena-and-aws-glue-to-accelerate-downstream-queries/)」を参照してください。

# データのパーティション化
<a name="partitions"></a>

データをパーティションすることで、各クエリによってスキャンされるデータの量を制限できるようになるため、パフォーマンスが向上し、コストが削減されます。任意のキーでデータをパーティションに分割することができます。一般的な方法では、時間に基づいてデータをパーティションします。これにより、通常、複数レベルのパーティション構成となります。たとえば、1 時間ごとに配信されるデータを年、月、日、時間でパーティションできます。別の例として、データが配信されるソースが多数に分かれているものの、それらのロードは 1 日 1 回だけ行われる場合には、データソースと日付によるパーティションを行います。

Athena では Apache Hive スタイルのパーティションを使用できます。このパーティションのデータパスには、等号で連結されたキーと値のペア (例えば `country=us/...` または `year=2021/month=01/day=26/...`) が含まれています。つまり、それぞれのパスにより、パーティションのキーと値、両方の名前が表されます。新しい Hive パーティションをパーティションされたテーブルにロードするには、(Hive スタイルのパーティションのみで機能する) [MSCK REPAIR TABLE](msck-repair-table.md) コマンドを使用します。

Athena では、Hive 以外のスタイルのパーティション化スキームを使用することも可能です。たとえば、CloudTrail ログと Firehose 配信ストリームは、`data/2021/01/26/us/6fc7845e.json` など、日付部分に個別のパスコンポーネントを使用します。これらの Hive スタイルではないパーティションの場合、[ALTER TABLE ADD PARTITION](alter-table-add-partition.md) を使用して手動でパーティションを追加します。

## 考慮事項と制限事項
<a name="partitions-considerations-limitations"></a>

パーティションを使用する場合は、次の点に注意してください。
+ パーティションされたテーブルをクエリし、`WHERE` 句でパーティションを指定する場合、Athena はそのパーティションからのデータしかスキャンしません。
+ 多数のオブジェクトがあり、データがパーティションされていない Amazon S3 バケットに対してクエリを発行する場合、このようなクエリは Amazon S3 の `GET` リクエストレート制限に影響を及ぼし、Amazon S3 例外を引き起こす可能性があります。エラーを防ぐには、データをパーティションします。また、Amazon S3 のリクエストレートをチューニングすることも検討してください。詳細については、「[設計パターンのベストプラクティス: Simple Storage Service (Amazon S3) のパフォーマンスの最適化](https://docs.aws.amazon.com/AmazonS3/latest/userguide/request-rate-perf-considerations.html)」を参照してください。
+ Athena で使用されるパーティションの場所は、`s3` プロトコル (`s3://amzn-s3-demo-bucket/folder/` など) を使用する必要があります。Athena では、他のプロトコル (`s3a://amzn-s3-demo-bucket/folder/` など) を使用する場所は、そこにあるテーブルに対して `MSCK REPAIR TABLE` クエリを実行する場合にクエリが失敗する原因になります。
+ Simple Storage Service (Amazon S3) パスがキャメルケースではなく小文字になっていることを確認します (例えば、`userId` ではなく `userid`)。S3 パスがキャメルケースの場合、`MSCK REPAIR TABLE` は AWS Glue Data Catalog にパーティションを追加しません。詳細については、「[MSCK REPAIR TABLE](msck-repair-table.md)」を参照してください。
+ `MSCK REPAIR TABLE` がフォルダとそのサブフォルダの両方をスキャンして一致するパーティションスキームを検索するため、別個のテーブルのデータは別個のフォルダ階層に保存するようにしてください。例えば、テーブル 1 のデータが `s3://amzn-s3-demo-bucket1` にあり、テーブル 2 のデータが `s3://amzn-s3-demo-bucket1/table-2-data` にあるとします。両方のテーブルが文字列でパーティション分割されている場合、`MSCK REPAIR TABLE` はテーブル 2 のパーティションをテーブル 1 に追加します。これを回避するには、この代わりに `s3://amzn-s3-demo-bucket2` や `s3://amzn-s3-demo-bucket1` といった別個のフォルダ構造を使用します。この動作は、Amazon EMR および Apache Hive と同じであることに注意してください。
+ Athena で AWS Glue Data Catalog を使用している場合は、「[AWS Glue エンドポイントとクォータ](https://docs.aws.amazon.com/general/latest/gr/glue.html)」を参照し、アカウントごとおよびテーブルごとのパーティションに関するサービスクォータをご確認ください。
  + Athena では、1,000 万のパーティションを持つ AWS Glue テーブルへのクエリがサポートされていますが、1 回のスキャンで読み取れるのは、100 万のパーティションまでです。このようなシナリオでは、パーティションのインデックス作成が役立ちます。詳細については、「AWS Big Data Blog」の記事「[Improve Amazon Athena query performance using AWS Glue Data Catalog partition indexes](https://aws.amazon.com/blogs/big-data/improve-amazon-athena-query-performance-using-aws-glue-data-catalog-partition-indexes/)」を参照してください。
+ AWS Glue Data Catalog を使用している場合にパーティションのクォータの引き上げをリクエストするには、[AWS Glue の Service Quotas コンソール](https://console.aws.amazon.com/servicequotas/home?region=us-east-1#!/services/glue/quotas) を参照してください。

## パーティショニングされたデータを含むテーブルを作成およびロードする
<a name="partitions-creating-loading"></a>

パーティションを使用するテーブルを作成するには、[CREATE TABLE](create-table.md) ステートメントの中で `PARTITIONED BY` 句を使用します。次の例のように、`PARTITIONED BY` はデータのパーティションに使用するキーを定義します。`LOCATION` 句では、パーティションされたデータのルートロケーションを指定します。

```
CREATE EXTERNAL TABLE users (
first string,
last string,
username string
)
PARTITIONED BY (id string)
STORED AS parquet
LOCATION 's3://amzn-s3-demo-bucket'
```

テーブルを作成した後、クエリ用にパーティションにデータをロードします。Hive スタイルのパーティションでは、[MSCK REPAIR TABLE](msck-repair-table.md) を実行します。Hive スタイルではないパーティションの場合、[ALTER TABLE ADD PARTITION](alter-table-add-partition.md) を使用して手動でパーティションを追加します。

## クエリのために Hive スタイルと非 Hive スタイルのデータを準備する
<a name="partitions-preparing-data"></a>

以下のセクションでは、Hive スタイルおよび非 Hive スタイルのデータを、Athena でのクエリ向けに準備する方法について説明します。

### シナリオ 1: Amazon S3 に Hive 形式で保存されているデータの場合
<a name="scenario-1-data-already-partitioned-and-stored-on-s3-in-hive-format"></a>

このシナリオでは、パーティションは Amazon S3 内で別々のフォルダに保存されています。例として、[https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3/ls.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3/ls.html) コマンドが出力した、サンプル広告のインプレッションに関するリスティングの一部を以下に示します。指定されたプレフィックスの下に、S3 オブジェクトがリストされています。

```
aws s3 ls s3://elasticmapreduce/samples/hive-ads/tables/impressions/

    PRE dt=2009-04-12-13-00/
    PRE dt=2009-04-12-13-05/
    PRE dt=2009-04-12-13-10/
    PRE dt=2009-04-12-13-15/
    PRE dt=2009-04-12-13-20/
    PRE dt=2009-04-12-14-00/
    PRE dt=2009-04-12-14-05/
    PRE dt=2009-04-12-14-10/
    PRE dt=2009-04-12-14-15/
    PRE dt=2009-04-12-14-20/
    PRE dt=2009-04-12-15-00/
    PRE dt=2009-04-12-15-05/
```

この例では、ログを保存する列名 (dt) が日付、時、分の増分と等しくなるように設定されています。親フォルダの場所、スキーマ、およびパーティション分割された列の名前を指定して DDL を提供すると、Athena はこれらのサブフォルダのデータをクエリできます。

#### テーブルを作成する
<a name="creating-a-table"></a>

このデータからテーブルを作成するには、以下の Athena DDL ステートメントにあるように、「dt」を使用してパーティションを作成します。

```
CREATE EXTERNAL TABLE impressions (
    requestBeginTime string,
    adId string,
    impressionId string,
    referrer string,
    userAgent string,
    userCookie string,
    ip string,
    number string,
    processId string,
    browserCookie string,
    requestEndTime string,
    timers struct<modelLookup:string, requestTime:string>,
    threadId string,
    hostname string,
    sessionId string)
PARTITIONED BY (dt string)
ROW FORMAT  serde 'org.apache.hive.hcatalog.data.JsonSerDe'
LOCATION 's3://elasticmapreduce/samples/hive-ads/tables/impressions/' ;
```

このテーブルは、Hive のネイティブ JSON シリアライザー/デシリアライザーを使用して Amazon S3 に保存された JSON データを読み込みます。サポートされる形式の詳細については、「[データ用に SerDe を選択する](supported-serdes.md)」を参照してください。

#### MSCK REPAIR TABLE を実行する
<a name="run-msck-repair-table"></a>

`CREATE TABLE` クエリの実行後、以下の例のように Athena クエリエディタで `MSCK REPAIR TABLE` コマンドを実行し、パーティションをロードします。

```
MSCK REPAIR TABLE impressions
```

このコマンドを実行すると、データにクエリするための準備が整います。

#### データのクエリ
<a name="query-the-data"></a>

パーティション列を使用して、インプレッションテーブルのデータをクエリします。例を示します。

```
SELECT dt,impressionid FROM impressions WHERE dt<'2009-04-12-14-00' and dt>='2009-04-12-13-00' ORDER BY dt DESC LIMIT 100
```

このクエリにより、次のような結果が表示されます。

```
2009-04-12-13-20    ap3HcVKAWfXtgIPu6WpuUfAfL0DQEc
2009-04-12-13-20    17uchtodoS9kdeQP1x0XThKl5IuRsV
2009-04-12-13-20    JOUf1SCtRwviGw8sVcghqE5h0nkgtp
2009-04-12-13-20    NQ2XP0J0dvVbCXJ0pb4XvqJ5A4QxxH
2009-04-12-13-20    fFAItiBMsgqro9kRdIwbeX60SROaxr
2009-04-12-13-20    V4og4R9W6G3QjHHwF7gI1cSqig5D1G
2009-04-12-13-20    hPEPtBwk45msmwWTxPVVo1kVu4v11b
2009-04-12-13-20    v0SkfxegheD90gp31UCr6FplnKpx6i
2009-04-12-13-20    1iD9odVgOIi4QWkwHMcOhmwTkWDKfj
2009-04-12-13-20    b31tJiIA25CK8eDHQrHnbcknfSndUk
```

### シナリオ 2: データが Hive 形式でパーティション化されない
<a name="scenario-2-data-is-not-partitioned"></a>

次の例では `aws s3 ls` コマンドにより、Amazon S3 に保存されている [ELB](elasticloadbalancer-classic-logs.md) ログを表示します。このデータレイアウトでは `key=value` ペアを使用しておらず、つまり、Hive 形式ではないという点に注意してください。(`aws s3 ls` コマンドの `--recursive` オプションは、指定したディレクトリまたはプレフィックスに含まれる、すべてのファイルまたはオブジェクトを一覧表示するように指定します。)

```
aws s3 ls s3://athena-examples-myregion/elb/plaintext/ --recursive

2016-11-23 17:54:46   11789573 elb/plaintext/2015/01/01/part-r-00000-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:46    8776899 elb/plaintext/2015/01/01/part-r-00001-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:46    9309800 elb/plaintext/2015/01/01/part-r-00002-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:47    9412570 elb/plaintext/2015/01/01/part-r-00003-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:47   10725938 elb/plaintext/2015/01/01/part-r-00004-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:46    9439710 elb/plaintext/2015/01/01/part-r-00005-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:47          0 elb/plaintext/2015/01/01_$folder$
2016-11-23 17:54:47    9012723 elb/plaintext/2015/01/02/part-r-00006-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:47    7571816 elb/plaintext/2015/01/02/part-r-00007-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:47    9673393 elb/plaintext/2015/01/02/part-r-00008-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:48   11979218 elb/plaintext/2015/01/02/part-r-00009-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:48    9546833 elb/plaintext/2015/01/02/part-r-00010-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:48   10960865 elb/plaintext/2015/01/02/part-r-00011-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:48          0 elb/plaintext/2015/01/02_$folder$
2016-11-23 17:54:48   11360522 elb/plaintext/2015/01/03/part-r-00012-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:48   11211291 elb/plaintext/2015/01/03/part-r-00013-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:48    8633768 elb/plaintext/2015/01/03/part-r-00014-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:49   11891626 elb/plaintext/2015/01/03/part-r-00015-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:49    9173813 elb/plaintext/2015/01/03/part-r-00016-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:49   11899582 elb/plaintext/2015/01/03/part-r-00017-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:49          0 elb/plaintext/2015/01/03_$folder$
2016-11-23 17:54:50    8612843 elb/plaintext/2015/01/04/part-r-00018-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:50   10731284 elb/plaintext/2015/01/04/part-r-00019-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:50    9984735 elb/plaintext/2015/01/04/part-r-00020-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:50    9290089 elb/plaintext/2015/01/04/part-r-00021-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:50    7896339 elb/plaintext/2015/01/04/part-r-00022-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51    8321364 elb/plaintext/2015/01/04/part-r-00023-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51          0 elb/plaintext/2015/01/04_$folder$
2016-11-23 17:54:51    7641062 elb/plaintext/2015/01/05/part-r-00024-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51   10253377 elb/plaintext/2015/01/05/part-r-00025-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51    8502765 elb/plaintext/2015/01/05/part-r-00026-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51   11518464 elb/plaintext/2015/01/05/part-r-00027-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51    7945189 elb/plaintext/2015/01/05/part-r-00028-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51    7864475 elb/plaintext/2015/01/05/part-r-00029-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51          0 elb/plaintext/2015/01/05_$folder$
2016-11-23 17:54:51   11342140 elb/plaintext/2015/01/06/part-r-00030-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51    8063755 elb/plaintext/2015/01/06/part-r-00031-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:52    9387508 elb/plaintext/2015/01/06/part-r-00032-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:52    9732343 elb/plaintext/2015/01/06/part-r-00033-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:52   11510326 elb/plaintext/2015/01/06/part-r-00034-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:52    9148117 elb/plaintext/2015/01/06/part-r-00035-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:52          0 elb/plaintext/2015/01/06_$folder$
2016-11-23 17:54:52    8402024 elb/plaintext/2015/01/07/part-r-00036-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:52    8282860 elb/plaintext/2015/01/07/part-r-00037-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:52   11575283 elb/plaintext/2015/01/07/part-r-00038-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:53    8149059 elb/plaintext/2015/01/07/part-r-00039-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:53   10037269 elb/plaintext/2015/01/07/part-r-00040-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:53   10019678 elb/plaintext/2015/01/07/part-r-00041-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:53          0 elb/plaintext/2015/01/07_$folder$
2016-11-23 17:54:53          0 elb/plaintext/2015/01_$folder$
2016-11-23 17:54:53          0 elb/plaintext/2015_$folder$
```

#### ALTER TABLE ADD PARTITION を実行する
<a name="run-alter-table-add-partition"></a>

ここでのデータは Hive 形式ではないため、パーティションの作成後に、テーブルにパーティションを追加するために `MSCK REPAIR TABLE` コマンドを使用することはできません。代わりに [ALTER TABLE ADD PARTITION](alter-table-add-partition.md) コマンドを使用することで、各パーティションを手動で追加できます。例えば、s3://athena-examples-*myregion*/elb/plaintext/2015/01/01/ のデータをロードするには、次のクエリを実行します。Amazon S3 フォルダごとに個別のパーティション列は必要ないこと、およびパーティションキーの値が Amazon S3 キーと同じではない場合があることに注意してください。

```
ALTER TABLE elb_logs_raw_native_part ADD PARTITION (dt='2015-01-01') location 's3://athena-examples-us-west-1/elb/plaintext/2015/01/01/'
```

パーティションが既に存在する場合は、Partition already exists というエラーが表示されます。このエラーを回避するには、`IF NOT EXISTS` 句を使用することができます。詳細については、「[ALTER TABLE ADD PARTITION](alter-table-add-partition.md)」を参照してください。パーティションを削除する場合は、[ALTER TABLE DROP PARTITION](alter-table-drop-partition.md) を使用します。

## パーティション射影を検討する
<a name="partitions-partition-projection"></a>

パーティション射影を使用すると、自分自身でのパーティション管理の必要性を回避できます。パーティション射影は、高度にパーティション化されていて、その構造が既知のテーブルで使用できるオプションです。パーティション射影では、パーティションの値と場所はメタデータリポジトリから読み取られるのではなく、テーブルのプロパティ設定を基に算出されます。メモリ内での計算処理は、リモートルックアップよりも高速であるため、パーティション射影を使用することでクエリランタイムが大幅に短縮されます。

詳細については、「[Amazon Athena でパーティション射影を使用する](partition-projection.md)」を参照してください。

## その他のリソース
<a name="partitions-additional-resources"></a>
+ Firehose データのパーティショニングオプションについては、「[Amazon Data Firehose 例](partition-projection-kinesis-firehose-example.md)」を参照してください。
+ [JDBC ドライバー](connect-with-jdbc.md)を使用して、パーティションの追加を自動化できます。
+ CTAS と INSERT INTO を使用して、データセットをパーティションできます。詳細については、「[ETL およびデータ分析での CTAS および INSERT INTO を使用する](ctas-insert-into-etl.md)」を参照してください。

# Amazon Athena でパーティション射影を使用する
<a name="partition-projection"></a>

Athena では、高度にパーティションされたテーブルのクエリ処理を高速化し、パーティション管理を自動化するためにパーティション射影を使用できます。

パーティション射影では、Athena は AWS Glue のテーブルに直接設定したテーブルプロパティを使用してパーティション値と場所を計算します。テーブルプロパティにより、Athena は AWS Glue Data Catalogで時間をかけてメタデータを検索しなくても、必要なパーティション情報を「射影」または決定できます。多くの場合、インメモリオペレーションはリモートオペレーションよりも高速であるため、パーティション射影は高度にパーティションされたテーブルに対するクエリの実行時間を短縮できます。クエリおよび基盤となるデータの特定の特性によっては、パーティション射影によって、パーティションメタデータの取得時に制限されているクエリのクエリランタイムが大幅に短縮されます。

## パーティションプルーニングとパーティション射影を理解する
<a name="partition-projection-pruning-vs-projection"></a>

パーティションプルーニングは、メタデータを収集し、クエリに適用されるパーティションのみに「プルーニング」します。多くの場合、これによってクエリが高速化されます。Athena は、パーティション射影用に設定されたテーブルなどのパーティション列があるすべてのテーブルに対してパーティションプルーニングを使用します。

通常、クエリを処理する場合、Athena はパーティションプルーニングを実行する前に AWS Glue Data Catalog に対して `GetPartitions` 呼び出しを行います。テーブルに多数のパーティションがある場合、`GetPartitions` を使用すると、パフォーマンスに悪影響が及ぶ可能性があります。これを回避するには、パーティション射影を使用します。パーティション射影を設定すると、Athena が独自にパーティションを構築するために必要なすべての情報を得ることができるため、`GetPartitions` を呼び出す必要がなくなります。

## パーティション射影の使用方法
<a name="partition-projection-using"></a>

パーティション射影を使用するには、AWS Glue Data Catalog または [外部 Hive メタストア](connect-to-data-source-hive.md)のテーブルプロパティで、各パーティション列のパーティション値と射影型の範囲を指定します。テーブル上のこれらのカスタムプロパティは、Athena がテーブルでクエリを実行するときに、どのパーティションパターンを期待すべきかを把握できるようにします。クエリの実行中、Athena は、この情報を使用して、パーティション値を射影します。AWS Glue Data Catalog または外部 Hive メタストアから取得するのではありません。これにより、クエリの実行時間が短縮されるだけでなく、Athena、AWS Glue、または外部の Hive メタストアでパーティションを手動で作成する必要がなくなるため、パーティション管理も自動化されます。

**重要**  
テーブルでパーティション射影を有効にすると、AWS Glue Data Catalog または Hive メタストア内のテーブルに登録されているパーティションメタデータが Athena によりすべて無視されます。

## 一部のユースケース
<a name="partition-projection-use-cases"></a>

パーティション射影が役立つシナリオには、次のようなものがあります。
+ 高度にパーティション化されたテーブルに対するクエリが、思ったほどすぐに完了しない。
+ データに新しい日付または時刻パーティションが作成されたとき、定期的にパーティションをテーブルに追加する。パーティション射影で、新しいデータが到着したときに使用できる相対日付範囲を設定している。
+ Amazon S3 に高度にパーティションされたデータがある。データは、AWS Glue Data Catalog または Hive メタストア内のモデルに対して実用的ではなく、クエリがそのごく一部のみを読み込む。

### 射影可能なパーティション構造
<a name="partition-projection-known-data-structures"></a>

パーティション射影は、パーティションが次のような予測可能なパターン (ただし、これに限りません) に従う場合に最も簡単に設定できます。
+ **整数** – `[1, 2, 3, 4, ..., 1000]` または `[0500, 0550, 0600, ..., 2500]` などの整数の連続的なシーケンス。
+ **日付** – `[20200101, 20200102, ..., 20201231]` または `[1-1-2020 00:00:00, 1-1-2020 01:00:00, ..., 12-31-2020 23:00:00]` などの日付や日時の連続的なシーケンス。
+ **列挙値** – 空港コードや AWS リージョン など、列挙値の有限集合。
+ **AWS のサービス ログ** – AWS のサービス ログは通常、AWS Glue で指定できるので Athena がパーティション射影に使用できるパーティションスキームを持つ既知の構造になっています。

### パーティションパステンプレートをカスタマイズする方法
<a name="partition-projection-custom-s3-storage-locations"></a>

デフォルトで、Athena はフォーム `s3://amzn-s3-demo-bucket/<table-root>/partition-col-1=<partition-col-1-val>/partition-col-2=<partition-col-2-val>/` を使用してパーティションの場所を構築しますが、データの編成が異なる場合、Athena はこのパステンプレートをカスタマイズするためのメカニズムを提供します。手順については、「[カスタム S3 ストレージの場所を指定する方法](partition-projection-setting-up.md#partition-projection-specifying-custom-s3-storage-locations)」を参照してください。

## 考慮事項と制限事項
<a name="partition-projection-considerations-and-limitations"></a>

以下の考慮事項に注意してください。
+ パーティション射影により、AWS Glue または外部 Hive メタストアでパーティションを手動で指定する必要がなくなります。
+ テーブルでパーティション射影を有効にすると、そのテーブルについては、Athena は AWS Glue Data Catalog 内または外部 Hive メタストア内のパーティションメタデータを無視します。
+ 射影されたパーティションが Amazon S3 に存在しない場合でも、Athena はそのパーティションを射影します。Athena はエラーをスローし、データを返しません。ただし、空のパーティションが多すぎる場合、従来の AWS Glue パーティションに比べてパフォーマンスが低下する可能性があります。射影パーティションの半分以上が空の場合は、従来のパーティションを使用することをお勧めします。
+ パーティション射影に定義された範囲境界を超える値のクエリは、エラーを返しません。その代わりに、クエリが実行されても行は返されません。たとえば、2020 年に開始される時間関連データがあり、`'projection.timestamp.range'='2020/01/01,NOW'` として定義されている場合、`SELECT * FROM table-name WHERE timestamp = '2019/02/02'` のようなクエリは正常に完了しますが、行は返されません。
+ パーティション射影は、テーブルが Athena を通じてクエリされる場合以外は使用できません。同じテーブルが Amazon Redshift Spectrum、Athena for Spark、Amazon EMR などの別のサービスを介して読み取られる場合、標準のパーティションメタデータが使用されます。
+ パーティション射影は DML 限定の機能であるため、`SHOW PARTITIONS` は、Athena によって射影されていても、AWS Glue カタログまたは外部の Hive メタストアには登録されていないパーティションをリストしません。
+ Athena は、パーティションの射影の設定としてビューのテーブルプロパティを使用しません。この制限を回避するには、ビューが参照するテーブルのテーブルプロパティでパーティション射影を構成して有効にします。

## 動画
<a name="partition-projection-video"></a>

以下の動画は、パーティション射影を使用して Athena でのクエリのパフォーマンスを向上させる方法を紹介するものです。

[![AWS Videos](http://img.youtube.com/vi/https://www.youtube.com/embed/iUD5pPpcyZk/0.jpg)](http://www.youtube.com/watch?v=https://www.youtube.com/embed/iUD5pPpcyZk)


**Topics**
+ [パーティションプルーニングとパーティション射影を理解する](#partition-projection-pruning-vs-projection)
+ [パーティション射影の使用方法](#partition-projection-using)
+ [一部のユースケース](#partition-projection-use-cases)
+ [考慮事項と制限事項](#partition-projection-considerations-and-limitations)
+ [動画](#partition-projection-video)
+ [パーティション射影を設定する](partition-projection-setting-up.md)
+ [パーティション射影用にサポートされている型](partition-projection-supported-types.md)
+ [動的な ID パーティショニングを使用する](partition-projection-dynamic-id-partitioning.md)
+ [Amazon Data Firehose 例](partition-projection-kinesis-firehose-example.md)

# パーティション射影を設定する
<a name="partition-projection-setting-up"></a>

テーブルのプロパティでのパーティション射影のセットアップは、次の 2 つのステップで行います。

1. 各パーティション列のデータ範囲と関連するパターンを指定するか、カスタムテンプレートを使用します。

1. テーブルのパーティション射影を有効にします。

**注記**  
パーティション射影プロパティを既存のテーブルに追加する前に、パーティション射影プロパティをセットアップするパーティションの列がテーブルスキーマにすでに存在している必要があります。パーティション列がまだ存在しない場合、既存のテーブルに手動でパーティション列を追加する必要があります。AWS Glue では、この手順を自動的に実行しません。

このセクションでは、AWS Glue にテーブルプロパティを設定する方法について説明します。これらを設定するには、AWS Glue コンソール、Athena [CREATE TABLE](create-table.md) クエリ、または [AWS Glue API](https://docs.aws.amazon.com/glue/latest/dg/aws-glue-api.html) オペレーションを使用できます。次の手順は、AWS Glue コンソールでプロパティを設定する方法を示しています。

**AWS Glue コンソールを使用してパーティション射影を設定して有効化する**

1. AWS マネジメントコンソール にサインインし、AWS Glue コンソール ([https://console.aws.amazon.com/glue/](https://console.aws.amazon.com/glue/)) を開きます。

1. [**Tables**] (テーブル) タブを選択します。

   **[Tables]** (テーブル) タブでは、既存のテーブルを編集する、または **[Add tables]** (テーブルの追加) を選択して新しいテーブルを作成できます。テーブルを手動またはクローラで追加する方法については、「AWS Glue デベロッパーガイド」の「[AWS Glue コンソールでのテーブルの使用](https://docs.aws.amazon.com/glue/latest/dg/console-tables.html)」を参照してください。

1. テーブルのリストで、編集するテーブルのリンクを選択します。  
![\[AWS Glue コンソールで、編集するテーブルを選択します。\]](http://docs.aws.amazon.com/ja_jp/athena/latest/ug/images/partition-projection-1.png)

1. **[Actions]** (アクション)、**[Edit table]** (テーブルの編集) の順に選択します。

1. **[Edit table]** (テーブルの編集) ページの **[Table properties]** (テーブルプロパティ) セクションで、パーティション化された列ごとに以下のキーと値のペアを追加します。

   1. **[Key]** (キー)に、`projection.columnName.type` を追加します。

   1. [**値**] に、サポートされている型 (`enum`、`integer`、`date`、`injected`) のいずれかを追加します。詳細については、「[パーティション射影用にサポートされている型](partition-projection-supported-types.md)」(パーティション射影に対応した型) を参照してください。

1. 「[パーティション射影用にサポートされている型](partition-projection-supported-types.md)」のガイダンスに従って、設定要件に従って追加のキーと値のペアを追加します。

   次のテーブル設定例では、パーティション射影用に `year` 列を設定し、返すことのできる値を 2010～2016 の範囲に制限します。  
![\[AWS Glue コンソールのテーブルプロパティでパーティション列のパーティション射影を構成。\]](http://docs.aws.amazon.com/ja_jp/athena/latest/ug/images/partition-projection-3.png)

1. キーと値のペアを追加して、パーティション射影を有効にします。[**Key**] (キー) には `projection.enabled` と入力し、その [**Value**] (値) には `true` と入力します。
**注記**  
`projection.enabled` を `false` に設定することで、このテーブルのパーティション射影をいつでも無効にできます。

1. 完了したら、[**Save **] を選択します。

1. Athena クエリエディタで、テーブル用に設定した列でのクエリをテストします。

   次のクエリ例では、`SELECT DISTINCT` を使用して、`year` 列から一意の値を返します。データベースには 1987 年から 2016 年のデータが含まれていますが、`projection.year.range` プロパティで返される値が 2010 年から 2016 年に制限されています。  
![\[パーティション射影を使用する列のクエリ。\]](http://docs.aws.amazon.com/ja_jp/athena/latest/ug/images/partition-projection-5.png)
**注記**  
`projection.enabled` を `true` に設定したが、1 つ以上のパーティション列を設定できない場合は、次のようなエラーメッセージが表示されます。  
`HIVE_METASTORE_ERROR: Table database_name.table_name is configured for partition projection, but the following partition columns are missing projection configuration: [column_name] (table database_name.table_name)`.

## カスタム S3 ストレージの場所を指定する方法
<a name="partition-projection-specifying-custom-s3-storage-locations"></a>

AWS Glue でテーブルプロパティを編集するときは、射影されたパーティションのカスタム Amazon S3 パステンプレートを指定することもできます。カスタムテンプレートは、Athena が通常の `.../column=value/...` パターンに従わないカスタム Amazon S3 ファイルの場所に、パーティション値を適切にマッピングすることを可能にします。

カスタムテンプレートの使用はオプションです。ただし、カスタムテンプレートを使用する場合、各パーティション列のプレースホルダをテンプレートに含める必要があります。テンプレート化された場所は、パーティション化されたデータファイルがパーティションごとの「フォルダ」に格納されるように、スラッシュで終わる必要があります。

**カスタムパーティションの場所のテンプレートを指定するには**

1. 手順に従って、[AWS Glue コンソールを使用して、パーティション射影の設定と有効化を行い](#partition-projection-setting-up-procedure)、次のようにカスタムテンプレートを指定するキーと値のペアを追加します。

   1. [**キー**] に「`storage.location.template`」と入力します。

   1. [**値**] で、すべてのパーティション列のプレースホルダを含む場所を指定します。各プレースホルダ (および S3 パス自体) が 1 つのスラッシュで終了していることを確認します。

      次のテンプレート値の例では、パーティション列 `a`、`b`、`c` があるテーブルを想定しています。

      ```
      s3://amzn-s3-demo-bucket/table_root/a=${a}/${b}/some_static_subdirectory/${c}/      
      ```

      ```
      s3://amzn-s3-demo-bucket/table_root/c=${c}/${b}/some_static_subdirectory/${a}/${b}/${c}/${c}/      
      ```

      同じテーブルの場合、次のテンプレート値の例は、列 `c` のプレースホルダが含まれていないため無効です。

      ```
      s3://amzn-s3-demo-bucket/table_root/a=${a}/${b}/some_static_subdirectory/         
      ```

1. [**Apply**] を選択します。

# パーティション射影用にサポートされている型
<a name="partition-projection-supported-types"></a>

テーブルには、`enum`、`integer`、`date,`、または `injected` パーティション列の型の任意の組み合わせを使用できます。

## 列挙型
<a name="partition-projection-enum-type"></a>

値が列挙セット (空港コードや AWS リージョン など) のメンバーであるパーティション列には、`enum` 型を使用します。

テーブルでパーティションプロパティを次のように定義します。


****  

| プロパティ名 | 値の例 | 説明 | 
| --- | --- | --- | 
| projection.columnName.type |  `enum`  | 必須。列 columnName に使用する射影型です。列挙型の使用を通知するには、値を enum (大文字と小文字を区別しない) にする必要があります。先頭と末尾に空白文字を使用できます。 | 
| projection.columnName.values |  `A,B,C,D,E,F,G,Unknown`  | 必須。列 columnName に対する列挙されたパーティション値のカンマ区切りリスト。空白は列挙値の一部とみなされます。 | 

**注記**  
ベストプラクティスとして、`enum` ベースのパーティション射影の使用は数 10 個以下に制限することが推奨されます。`enum` 射影に固有の制限はありませんが、テーブルのメタデータを gzip に圧縮するときには、合計サイズが約 1MB という AWS Glue の制限を超えることはできません。この制限は、列名、場所、およびストレージ形式といったテーブルの主要部分全体で共有されることに注意してください。`enum` 射影で使用している一意の ID が数十個ある場合は、サロゲートフィールドにバケット化する一意の値を少なくするなど別のアプローチを検討してください。カーディナリティをトレードオフすることで、`enum` フィールドの一意の値の数を制御できます。

## 整数型
<a name="partition-projection-integer-type"></a>

使用できる値が定義された範囲内の整数として解釈可能なパーティション列には、整数型を使用します。射影整数列は現在、Java 符号付き長整数値 (-263 ～ 263-1。両端の値を含みます) の範囲に制限されています。


****  

| プロパティ名 | 値の例 | 説明 | 
| --- | --- | --- | 
| projection.columnName.type |  `integer`  | 必須。列 columnName に使用する射影型です。整数型の使用を通知するには、値を integer (大文字と小文字を区別しない) にする必要があります。先頭と末尾に空白文字を使用できます。 | 
| projection.columnName.range |  `0,10` `-1,8675309` `0001,9999`  | 必須。columnName 列のクエリによって返される最小値と最大値の範囲を示す 2 要素カンマ区切りリスト。値はハイフンではなくカンマで区切る必要があることに注意してください。両端の値が含まれ、負の値でもかまいません。また、先頭をゼロにすることもできます。先頭と末尾に空白文字を使用できます。 | 
| projection.columnName.interval |  `1` `5`  | オプション。列 columnName の連続するパーティション値の間隔を指定する正の整数。たとえば、range 値が「1,3」で、interval 値が「1」の場合、値 1、2、3 が生成されます。range 値が同じで interval が「2」の場合、値 1 と 3 が生成され、2 はスキップされます。先頭と末尾に空白文字を使用できます。デフォルトは 1 です。 | 
| projection.columnName.digits |  `1` `5`  | オプション。列 columnName のパーティション値の最終表現に含める桁数を指定する正の整数。たとえば、range 値が「1,3」で digits 値が「1」の場合、値 1、2、3 が生成されます。range 値が同じで digits 値が「2」の場合、値 01、02、03 が生成されます。先頭と末尾に空白文字を使用できます。デフォルトでは、静的な桁数も先頭のゼロもありません。 | 

## 日付型
<a name="partition-projection-date-type"></a>

定義された範囲内の日付 (オプションで時刻を含む) として解釈可能な値を持つパーティション列には、日付型を使用します。

**重要**  
射影される日付列は、クエリの実行時に協定世界時 (UTC) で生成されます。


****  

| プロパティ名 | 値の例 | 説明 | 
| --- | --- | --- | 
| projection.columnName.type |  `date`  | 必須。列 columnName に使用する射影型です。日付タイプの使用を通知するには、値を date (大文字と小文字を区別しない) にする必要があります。先頭と末尾に空白文字を使用できます。 | 
| projection.columnName.range |  `201701,201812` `01-01-2010,12-31-2018` `NOW-3YEARS,NOW` `201801,NOW+1MONTH`  |  必須。*columnName* 列の最小および最大 `range` 値を提供する、2 要素のカンマ区切りのリストです。両端の値が含まれ、Java の `java.time.*` 日付型と互換性のある任意の形式を使用できます。最小値と最大値のどちらも同じ形式を使用する必要があります。`.format` プロパティで指定する形式は、これらの値に使用される形式にする必要があります。 この列には、次の正規表現パターンの形式で相対日付文字列を含めることもできます。 `\s*NOW\s*(([\+\-])\s*([0-9]+)\s*(YEARS?\|MONTHS?\|WEEKS?\|DAYS?\|HOURS?\|MINUTES?\|SECONDS?)\s*)?` 空白は使用できますが、日付リテラルは日付文字列自体の一部と見なされます。  | 
| projection.columnName.format |  `yyyyMM` `dd-MM-yyyy` `dd-MM-yyyy-HH-mm-ss`  | 必須。Java の日付フォーマット [DateTimeFormatter](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html) に基づく日付形式文字列。サポートされている任意の Java.time.\$1 型を使用できます。 | 
| projection.columnName.interval |  `1` `5`  |  列 *columnName*の連続するパーティション値の間隔を指定する正の整数。たとえば、`range` 値が `2017-01,2018-12`、`interval` 値が `1`、`interval.unit` 値が `MONTHS` の場合、2017-01、2017-02、2017-03 というように値が生成されます。`range` 値が同じで `interval` 値が `2`、`interval.unit` 値が `MONTHS` の場合、2017-01、2017-03、2017-05 というように値が生成されます。先頭と末尾に空白文字を使用できます。 指定された日付が単一日または単一月の精度である場合、`interval` はオプションであり、デフォルトではそれぞれ 1 日または 1 か月です。それ以外の場合、`interval` が必要です。  | 
| projection.columnName.interval.unit |  `YEARS` `MONTHS` `WEEKS` `DAYS` `HOURS` `MINUTES` `SECONDS` `MILLIS`  |  [ChronoUnit](https://docs.oracle.com/javase/8/docs/api/java/time/temporal/ChronoUnit.html) のシリアル化された形式を表す時間単位の単語。使用できる値は `YEARS`、`MONTHS`、`WEEKS`、`DAYS`、`HOURS`、`MINUTES`、`SECONDS`、`MILLIS` です。これらの値の大文字と小文字は区別されません。 指定された日付が単一日または単一月の精度である場合、`interval.unit` はオプションであり、デフォルトではそれぞれ 1 日または 1 か月です。それ以外の場合は、`interval.unit` が必要です。  | 

**Example – 月によるパーティション**  
次のテーブル設定例では、2015 年から現在までのデータを月ごとにパーティション化しています。  

```
'projection.month.type'='date', 
'projection.month.format'='yyyy-MM', 
'projection.month.interval'='1', 
'projection.month.interval.unit'='MONTHS', 
'projection.month.range'='2015-01,NOW', 
...
```

## 挿入型
<a name="partition-projection-injected-type"></a>

使用できる値をいくつかの論理範囲内で手動で生成することができないが、クエリの `WHERE` 句で単一の値として指定されるパーティション列には、挿入型を使用します。

次の点に留意することが重要です。
+ 挿入列ごとにフィルタ式が指定されていない場合、挿入列でのクエリは失敗します。
+ 挿入された列のフィルター式に複数の値を持つクエリは、値が論理和である場合にのみ成功します。
+ `string` 型の列のみがサポートされています。
+ 挿入されたパーティション列で `WHERE IN` 句を使用する場合、`IN` リストで指定できる値は 1,000 個に制限されます。挿入された列のパーティションが 1,000 個を超えるデータセットをクエリするには、クエリを複数の小さなクエリに分割し、それぞれ `WHERE IN` 句に最大 1,000 個の値を指定して結果を集計します。


****  

| プロパティ名 | 値 | 説明 | 
| --- | --- | --- | 
| projection.columnName.type |  `injected`  | 必須。列 columnName に使用する射影型です。string 型のみがサポートされています。値 injected (大文字と小文字は区別されません) を指定する必要があります。先頭と末尾に空白文字を使用できます。 | 

詳細については、「[`injected` 射影タイプを使用すべき場合](partition-projection-dynamic-id-partitioning.md#partition-projection-injection)」を参照してください。

# 動的な ID パーティショニングを使用する
<a name="partition-projection-dynamic-id-partitioning"></a>

カーディナリティの高いプロパティによってデータが分割されている場合や、値が事前にわからない場合は、`injected` 射影型を使用できます。このようなプロパティの例としては、ユーザー名、デバイスや製品の ID などがあります。`injected` 射影型を使用してパーティションキーを設定すると、Athena はクエリ自体の値を使用して、読み取られるパーティションのセットを計算します。

Athena が `injected` プロジェクションタイプで設定されたパーティションキーを持つテーブルでクエリを実行できるようにするには、以下が満たされている必要があります。
+ クエリには、パーティションキーの値が少なくとも 1 つ含まれている必要があります。
+ 値は、データを読み取らなくても評価できるリテラルまたは式でなければなりません。

これらの基準のいずれかが満たされない場合、クエリは次のエラーで失敗します。

CONSTRAINT\$1VIOLATION: injected 射影型のパーティション列 *column\$1name* では、WHERE 句に静的な等号条件のみが含まれており、このような条件が少なくとも 1 つ存在する必要があります。

## `injected` 射影タイプを使用すべき場合
<a name="partition-projection-injection"></a>

IoT デバイスからのイベントで構成され、デバイスの ID で分割されたデータセットがあるとします。このデータセットには次の特徴があります。
+ デバイス ID はランダムに生成されています。
+ 新しいデバイスは頻繁にプロビジョニングされます。
+ 現在、数十万台のデバイスがあり、今後は数百万台になる見込みです。

このデータセットを従来のメタストアで管理するのは容易ではありません。データストレージとメタストア間でパーティションを同期させることは難しく、クエリ計画中はパーティションのフィルタリングに時間がかかることがあります。ただし、パーティションプロジェクションを使用するようにテーブルを設定し、`injected` 射影型を使用する場合には、メタストア内のパーティションを管理する必要がないことと、クエリでパーティションメタデータを検索する必要がないことの 2 つの利点があります。

次の `CREATE TABLE` の例では、先ほど説明したデバイスイベントデータセットのテーブルを作成します。このテーブルでは、injected 射影型を使用しています。

```
CREATE EXTERNAL TABLE device_events (
  event_time TIMESTAMP,
  data STRING,
  battery_level INT
)
PARTITIONED BY (
  device_id STRING
)
LOCATION "s3://amzn-s3-demo-bucket/prefix/"
TBLPROPERTIES (
  "projection.enabled" = "true",
  "projection.device_id.type" = "injected",
  "storage.location.template" = "s3://amzn-s3-demo-bucket/prefix/${device_id}"
)
```

次のクエリ例では、特定の 3 つのデバイスから 12 時間にわたって受信したイベントの数を調べます。

```
SELECT device_id, COUNT(*) AS events
FROM device_events
WHERE device_id IN (
  '4a770164-0392-4a41-8565-40ed8cec737e',
  'f71d12cf-f01f-4877-875d-128c23cbde17',
  '763421d8-b005-47c3-ba32-cc747ab32f9a'
)
AND event_time BETWEEN TIMESTAMP '2023-11-01 20:00' AND TIMESTAMP '2023-11-02 08:00'
GROUP BY device_id
```

このクエリを実行すると、Athena は `device_id` パーティションキーの 3 つの値を確認し、それらを使用してパーティションの場所を計算します。Athena は `storage.location.template` プロパティの値を使用して以下の場所を生成します。
+ `s3://amzn-s3-demo-bucket/prefix/4a770164-0392-4a41-8565-40ed8cec737e`
+ `s3://amzn-s3-demo-bucket/prefix/f71d12cf-f01f-4877-875d-128c23cbde17`
+ `s3://amzn-s3-demo-bucket/prefix/763421d8-b005-47c3-ba32-cc747ab32f9a`

パーティションの射影設定から `storage.location.template` プロパティを除外した場合、Athena は Hive 形式のパーティション分割を使用して、`LOCATION` の値 (例: `s3://amzn-s3-demo-bucket/prefix/device_id=4a770164-0392-4a41-8565-40ed8cec737e`) に基づいてパーティションの場所を射影します。

# Amazon Data Firehose 例
<a name="partition-projection-kinesis-firehose-example"></a>

Firehose を使用して Amazon S3 にデータを配信する場合、デフォルト設定は、次の例にあるようなキーを持つオブジェクトを書き込みます。

```
s3://amzn-s3-demo-bucket/prefix/yyyy/MM/dd/HH/file.extension
```

新しいデータが到着したときにパーティションを AWS Glue Data Catalog に追加する必要なく、クエリ時にパーティションを自動的に検索する Athena テーブルを作成するには、パーティション射影を使用できます。

以下の `CREATE TABLE` 例は、デフォルトの Firehose 設定を使用しています。

```
CREATE EXTERNAL TABLE my_ingested_data (
 ...
)
...
PARTITIONED BY (
 datehour STRING
)
LOCATION "s3://amzn-s3-demo-bucket/prefix/"
TBLPROPERTIES (
 "projection.enabled" = "true",
 "projection.datehour.type" = "date",
 "projection.datehour.format" = "yyyy/MM/dd/HH",
 "projection.datehour.range" = "2021/01/01/00,NOW",
 "projection.datehour.interval" = "1",
 "projection.datehour.interval.unit" = "HOURS",
 "storage.location.template" = "s3://amzn-s3-demo-bucket/prefix/${datehour}/"
)
```

`CREATE TABLE` ステートメントの `TBLPROPERTIES` 句は、Athena に次のように指定します。
+ テーブルのクエリ時にパーティション射影を使用する
+ パーティションキー `datehour` は `date` 型である (オプションで時刻を含む)
+ 日付の書式設定方法
+ 日付時刻の範囲。値はハイフンではなくカンマで区切る必要があることに注意してください。
+ Amazon S3 でのデータの検索先。

テーブルをクエリすると、Athena は `datehour` の値を計算し、ストレージロケーションテンプレートを使用して、パーティションの場所のリストを生成します。

**Topics**
+ [`date` タイプの使用方法](partition-projection-kinesis-firehose-example-using-the-date-type.md)
+ [パーティションキーの選択方法](partition-projection-kinesis-firehose-example-choosing-partition-keys.md)
+ [カスタムプレフィックスと動的パーティショニングの使用方法](partition-projection-kinesis-firehose-example-using-custom-prefixes-and-dynamic-partitioning.md)

# `date` タイプの使用方法
<a name="partition-projection-kinesis-firehose-example-using-the-date-type"></a>

射影パーティションキーに `date` 型を使用する場合、範囲を指定する必要があります。Firehose 配信ストリームが作成される前の日付に関するデータがないため、作成日を開始点として使用できます。また、将来の日付のデータがないため、最後として `NOW` という特別なトークンを使用できます。

`CREATE TABLE` の例では、開始日は 2021 年 1 月 1 日 UTC 午前 0 時と指定されています。

**注記**  
Athena が既存のパーティションのみを検索できるように、可能な限りデータと一致する範囲を設定します。

サンプルテーブルでクエリを実行すると、Athena は、`datehour`パーティションキーの条件を、値を生成する範囲と組み合わせて使用します。次のクエリについて考えます。

```
SELECT *
FROM my_ingested_data
WHERE datehour >= '2020/12/15/00'
AND datehour < '2021/02/03/15'
```

`SELECT` クエリの最初の条件では、`CREATE TABLE` ステートメントで指定された日付範囲の開始前の日付を使用しています。パーティション射影の設定では 2021 年 1 月 1 日より前の日付にはパーティションが指定されていないため、Athena は次の場所でのみデータを検索し、クエリ内のそれ以前の日付を無視します。

```
s3://amzn-s3-demo-bucket/prefix/2021/01/01/00/
s3://amzn-s3-demo-bucket/prefix/2021/01/01/01/
s3://amzn-s3-demo-bucket/prefix/2021/01/01/02/
...
s3://amzn-s3-demo-bucket/prefix/2021/02/03/12/
s3://amzn-s3-demo-bucket/prefix/2021/02/03/13/
s3://amzn-s3-demo-bucket/prefix/2021/02/03/14/
```

同様に、クエリが 2021 年 2 月 3 日 15:00 より前の日付と時刻で実行された場合、最後のパーティションに反映されているのは、クエリ条件の日時ではなく、現在の日付と時刻です。

最新のデータをクエリする場合は、Athena が将来の日付を生成しないという事実を活用し、次の例のように、開始 `datehour` のみを指定することができます。

```
SELECT *
FROM my_ingested_data
WHERE datehour >= '2021/11/09/00'
```

# パーティションキーの選択方法
<a name="partition-projection-kinesis-firehose-example-choosing-partition-keys"></a>

パーティション射影でパーティションの場所をパーティションキーにマッピングする方法を指定できます。前のセクションの `CREATE TABLE` の例では、日付と時刻を datehour という 1 つのパーティションキーにまとめましたが、他のスキームも可能です。例えば、パーティションキーを year、month、day、hour に分けてテーブルを構成することもできます。

ただし、日付を年、月、日に分割すると、`date` パーティション投影タイプは使用できなくなります。別の方法として、日付と時間を分離して `date` パーティション投影タイプを引き続き活用する方法がありますが、時間の範囲を指定するクエリは読みやすくします。

それを考慮して、次の `CREATE TABLE` の例では、日付と hour を分けています。`date` は SQL の予約語であるため、この例では日付を表すパーティションキーの名前として `day` を使用します。

```
CREATE EXTERNAL TABLE my_ingested_data2 (
 ...
)
...
PARTITIONED BY (
 day STRING,
 hour INT
)
LOCATION "s3://amzn-s3-demo-bucket/prefix/"
TBLPROPERTIES (
 "projection.enabled" = "true",
 "projection.day.type" = "date",
 "projection.day.format" = "yyyy/MM/dd",
 "projection.day.range" = "2021/01/01,NOW",
 "projection.day.interval" = "1",
 "projection.day.interval.unit" = "DAYS",
 "projection.hour.type" = "integer",
 "projection.hour.range" = "0,23",
 "projection.hour.digits" = "2",
 "storage.location.template" = "s3://amzn-s3-demo-bucket/prefix/${day}/${hour}/"
)
```

`CREATE TABLE` ステートメントの例では、hour は独立したパーティションキーで、整数に設定されています。hour パーティションキーの設定では、0～23 の範囲が指定され、Athena がパーティションの場所を生成するときに、hour を 2 桁でフォーマットする必要があります。

`my_ingested_data2` テーブルのクエリは次の例のようになります。

```
SELECT *
FROM my_ingested_data2
WHERE day = '2021/11/09'
AND hour > 3
```

## パーティションキーとパーティション射影データ型を理解する
<a name="partition-projection-kinesis-firehose-example-partition-key-types-and-partition-projection-types"></a>

最初の `CREATE TABLE` の例の `datehour` キーは、パーティション射影の設定では `date` として設定されていますが、パーティションキーの型は `string` なので、注意してください。2 番目の例の `day` についても同様です。パーティション射影に設定されている型は、Athena がパーティションの場所を生成するときの値のフォーマット方法のみを指示します。指定する型によって、パーティションキーの型は変更されません。クエリでは、`datehour` と `day` は `string` 型です。

クエリに `day = '2021/11/09'` のような条件が含まれている場合、Athena はパーティション射影設定で指定された日付形式を使用して、式の右辺の文字列を解析します。Athena は、日付が設定された範囲内であることを確認した後、日付形式を再び使用して日付を文字列として保存場所テンプレートに挿入します。

同様に、`day > '2021/11/09'` のようなクエリ条件の場合、Athena は右辺を解析し、設定された範囲内で一致するすべての日付のリストを生成します。次に、日付形式を使用して各日付を保存場所テンプレートに挿入し、パーティションの場所のリストを作成します。

`day > '2021-11-09'` や `day > DATE '2021-11-09'` と同じ条件を書くと動作しません。最初のケースでは、日付形式が一致せず (スラッシュではなくハイフンになっていることに注意してください)、2 番目のケースでは、データ型が一致しません。

# カスタムプレフィックスと動的パーティショニングの使用方法
<a name="partition-projection-kinesis-firehose-example-using-custom-prefixes-and-dynamic-partitioning"></a>

Firehose は、[カスタムプレフィックス](https://docs.aws.amazon.com/firehose/latest/dev/s3-prefixes.html)と[動的パーティショニング](https://docs.aws.amazon.com/firehose/latest/dev/dynamic-partitioning.html)を使用して設定できます。これらの機能を使用して、Amazon S3 キーを設定し、ユースケースをより適切にサポートするパーティション化スキームを設定できます。また、これらのパーティション化スキームでパーティション射影を使用し、それに応じて設定することもできます。

例えば、カスタムプレフィックス機能を使用して、デフォルトの `yyyy/MM/dd/HH` スキームではなく、ISO 形式の日付を持つ Amazon S3 キーを取得できます。

以下の例にあるように、カスタムプレフィックスと動的パーティショニングを組み合わせて、Firehose メッセージから `customer_id` のようなプロパティを抽出することもできます。

```
prefix/!{timestamp:yyyy}-!{timestamp:MM}-!{timestamp:dd}/!{partitionKeyFromQuery:customer_id}/
```

Amazon S3 プレフィックスを使用すると、Firehose 配信ストリームは、`s3://amzn-s3-demo-bucket/prefix/2021-11-01/customer-1234/file.extension` などのキーにオブジェクトを書き込みます。`customer_id` のようなプロパティの場合、事前に値が分かっていない場合もあるので、パーティション射影型 `injected` を使用し、次のような `CREATE TABLE` ステートメントを使用できます。

```
CREATE EXTERNAL TABLE my_ingested_data3 (
 ...
)
...
PARTITIONED BY (
 day STRING,
 customer_id STRING
)
LOCATION "s3://amzn-s3-demo-bucket/prefix/"
TBLPROPERTIES (
 "projection.enabled" = "true",
 "projection.day.type" = "date",
 "projection.day.format" = "yyyy-MM-dd",
 "projection.day.range" = "2021-01-01,NOW",
 "projection.day.interval" = "1",
 "projection.day.interval.unit" = "DAYS",
 "projection.customer_id.type" = "injected",
 "storage.location.template" = "s3://amzn-s3-demo-bucket/prefix/${day}/${customer_id}/"
)
```

`injected` 型のパーティションキーを持つテーブルに対してクエリを実行する場合、クエリにそのパーティションキーの値を含める必要があります。`my_ingested_data3` テーブルのクエリは次の例のようになります。

```
SELECT *
FROM my_ingested_data3
WHERE day BETWEEN '2021-11-01' AND '2021-11-30'
AND customer_id = 'customer-1234'
```

## day パーティションキーに DATE タイプを使用する
<a name="partition-projection-kinesis-firehose-example-iso-formatted-dates"></a>

`day` パーティションキーの値は ISO フォーマットされているので、次の例のように、day パーティションキーに、`STRING` ではなく、`DATE` 型を使用することもできます。

```
PARTITIONED BY (day DATE, customer_id STRING)
```

クエリを実行するとき、この戦略では、次の例のように、解析やキャストを行わずに、パーティションキーで日付関数を使用できます。

```
SELECT *
FROM my_ingested_data3
WHERE day > CURRENT_DATE - INTERVAL '7' DAY
AND customer_id = 'customer-1234'
```

**注記**  
`DATE` 型のパーティションキーを指定するには、[カスタムプレフィックス](https://docs.aws.amazon.com/firehose/latest/dev/s3-prefixes.html)機能を使用して ISO 形式の日付を含む Amazon S3 キーを作成したことを前提としています。デフォルトの Firehose 形式、`yyyy/MM/dd/HH` を使用している場合は、以下の例にあるように、対応するテーブルプロパティが `date` 型であっても、パーティションキーを `string` 型として指定する必要があります。  

```
PARTITIONED BY ( 
  `mydate` string)
TBLPROPERTIES (
  'projection.enabled'='true', 
   ...
  'projection.mydate.type'='date',
  'storage.location.template'='s3://amzn-s3-demo-bucket/prefix/${mydate}')
```

# Amazon S3 のスロットリングを防ぐ
<a name="performance-tuning-s3-throttling"></a>

スロットリングは、サービス、アプリケーション、またはシステムを使用する速度を制限するプロセスです。AWS では、スロットリングを使用して Amazon S3 サービスの過剰使用を防ぎ、すべてのユーザーの Amazon S3 の可用性と応答性を高めることができます。ただし、スロットリングによって Amazon S3 との間でデータを送受信できる速度が制限されるため、インタラクションが制限されるのを防ぐことを検討することが重要です。

[パフォーマンスチューニング](performance-tuning.md)の章で説明したように、最適化は、サービスレベルの決定、テーブルとデータの構造、クエリの記述方法によって異なります。

**Topics**
+ [サービスレベルでのスロットリングを軽減](performance-tuning-s3-throttling-reduce-throttling-at-the-service-level.md)
+ [テーブルを最適化する](performance-tuning-s3-throttling-optimizing-your-tables.md)
+ [クエリを最適化する](performance-tuning-s3-throttling-optimizing-queries.md)

# サービスレベルでのスロットリングを軽減
<a name="performance-tuning-s3-throttling-reduce-throttling-at-the-service-level"></a>

サービスレベルでの Amazon S3 スロットリングを回避するには、使用状況を監視して [Service Quotas](https://docs.aws.amazon.com/general/latest/gr/s3.html#limits_s3) を調整するか、パーティション化などの特定の手法を使用します。スロットリングの原因となる可能性があるいくつかの条件は、以下の通りです。
+ **[アカウントの API リクエスト制限の超過]** — Amazon S3 には、アカウントタイプと使用状況に基づくデフォルトの API リクエスト制限があります。1 つのプレフィックスの 1 秒あたりの最大リクエスト数を超えると、Amazon S3 サービスの過負荷を防ぐためにリクエストがスロットリングされることがあります。
+ **[データのパーティション分割が不十分]** — データを適切に分割せずに大量のデータを転送すると、Amazon S3 がリクエストを制限する場合があります。パーティション分割の詳細については、このドキュメントの「[パーティション分割を使用する](performance-tuning-s3-throttling-optimizing-your-tables.md#performance-tuning-s3-throttling-use-partitioning)」セクションを参照してください。
+ **[多数の小さいオブジェクト]** — 可能であれば、小さいファイルを多数使用することを避けてください。Amazon S3 には パーティション化されたプレフィックスごとに 1 秒あたり [5,500 件の GET リクエスト](https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance.html) という制限があり、Athena のクエリにもこれと同じ制限があります。単一のクエリで何百万もの小さなオブジェクトをスキャンする必要がある場合は、クエリが Amazon S3 によって制限される可能性が高くなります。

過度のスキャンを避けるには、AWS Glue ETL を使用してファイルを定期的に圧縮する、またはテーブルをパーティション化してパーティションキーフィルターを追加します。詳細については、以下のリソースを参照してください。
+ [より大きなファイルを出力するように AWS Glue ETL ジョブを設定する方法を教えてください。](https://aws.amazon.com/premiumsupport/knowledge-center/glue-job-output-large-files/)(AWS ナレッジセンター)
+ [大規模なグループでの入力ファイルの読み込み](https://docs.aws.amazon.com/glue/latest/dg/grouping-input-files.html) (AWS Glue 開発者ガイド)

# テーブルを最適化する
<a name="performance-tuning-s3-throttling-optimizing-your-tables"></a>

スロットリングの問題が発生した場合は、データを構造化することが重要です。Amazon S3 は大量のデータを処理できますが、データの構造が原因でスロットリングが発生することがあります。

以下のセクションでは、スロットリングの問題を回避するために Amazon S3 でデータを構造化するいくつかの方法について提案します。

## パーティション分割を使用する
<a name="performance-tuning-s3-throttling-use-partitioning"></a>

パーティション分割を使用すると、いつでもアクセスする必要があるデータ量を制限することで、スロットリングを減らすことができます。特定の列のデータをパーティション分割することで、複数のオブジェクトにリクエストを均等に分散し、1 つのオブジェクトに対するリクエスト数を減らすことができます。スキャンする必要のあるデータ量を減らすことは、クエリのパフォーマンスの向上とコストの削減につながります。

テーブルを作成するときに、仮想列として機能するパーティションを定義できます。`CREATE TABLE` ステートメント内にパーティションを含むテーブルを作成するには、`PARTITIONED BY (column_name data_type)` 句を使用してデータを分割するキーを定義します。

クエリによってスキャンされるパーティションを制限するには、クエリの `WHERE` 句にそれらを述語として指定できます。したがって、フィルターとして頻繁に使用される列はパーティション分割に適しています。一般的な方法では、時間間隔に基づいてデータをパーティション化します。これにより、通常、複数レベルのパーティション構成となります。

パーティション分割にはコストもかかることに注意してください。テーブル内のパーティション数を増やすと、パーティションメタデータの取得と処理に必要な時間も長くなります。そのため、パーティション分割が過剰になると、より計画的にパーティション分割を行うことで得られるメリットが得られない可能性があります。データが 1 つのパーティション値に大きく偏っていて、ほとんどのクエリがその値を使用している場合は、追加のオーバーヘッドが発生する可能性があります。

Athena でのパーティション分割の詳細については、「[パーティション化とは](ctas-partitioning-and-bucketing-what-is-partitioning.md)」を参照してください。

## データのバケット化
<a name="performance-tuning-s3-throttling-bucket-your-data"></a>

データを分割するもう 1 つの方法は、データを 1 つのパーティションにバケット化することです。バケット化するには、グループ化する行を含む 1 つまたは複数の列を指定します。次に、それらの行を複数のバケットに入れます。この方法では、読み取りが必要なバケットのみをクエリできるため、スキャンする必要のあるデータ行の数が減ります。

バケット化に使用する列を選択する際に、カーディナリティが高く (つまり、多数の異なる値があり)、均一に分布していて、データのフィルタリングによく使用する列を選択します。バケット化に適した列の例としては、ID 列などのプライマリキーがあります。

Athena でのバケット化の詳細については、「[バケット化とは](ctas-partitioning-and-bucketing-what-is-bucketing.md)」を参照してください。

## AWS Glue パーティションインデックスの使用
<a name="performance-tuning-s3-throttling-use-aws-glue-partition-indexes"></a>

AWS Glue パーティションインデックスを使用すると、1 つ以上のパーティション値に基づくテーブル内のデータを整理できます。AWS Glue パーティションインデックスを使用すると、データ転送回数、データ処理量、およびクエリの処理時間を削減できます。

AWS Glue パーティションインデックスは、パーティションキーとその値など、テーブル内のパーティションに関する情報を含むメタデータファイルです。パーティションインデックスは Amazon S3 バケットに保存され、新しいパーティションがテーブルに追加されると AWS Glue によって自動的に更新されます。

AWS Glue パーティションインデックスが存在する場合は。クエリでは、テーブル内のすべてのパーティションをロードするのではなく、パーティションのサブセットを取得しようとします。クエリは、クエリに関連するデータのサブセットでのみ実行されます。

AWS Glue でテーブルを作成する際は、テーブルで定義されたパーティションキーの任意の組み合わせに対して、パーティションインデックスを作成できます。テーブルに 1 つ以上のパーティションインデックスを作成したら、パーティションフィルタリングを有効にするプロパティをテーブルに追加する必要があります。その後、Athena からテーブルにクエリを実行できます。

AWS Glue でのパーティションインデックス作成のステップについては、「AWS Glue デベロッパーガイド」の「[AWS Glue でパーティションインデックスを使用する](https://docs.aws.amazon.com/glue/latest/dg/partition-indexes.html)」を参照してください。テーブルプロパティを追加してパーティションフィルタリングを有効にする方法については、「[AWS Glue パーティションのインデックス作成とフィルタリングでクエリを最適化する](glue-best-practices-partition-index.md)」を参照してください。

## データ圧縮とファイル分割の使用
<a name="performance-tuning-s3-throttling-use-data-compression-and-file-splitting"></a>

ファイルが最適なサイズである場合や、ファイルを論理グループに分割できる場合は、データ圧縮によりクエリを大幅に高速化できます。一般に、圧縮率が高いほど、データを圧縮および解凍するためにより多くの CPU サイクルが必要になります。Athena の場合は、デフォルトでデータを圧縮する Apache Parquet または Apache ORC のいずれかを使用することをお勧めします。Athena でのデータ圧縮の詳細については、「[Athena で圧縮を使用する](compression-formats.md)」を参照してください。

ファイルを分割すると、Athena は 1 つのファイルを読み取るタスクを複数のリーダーに分散できるため、並列処理が向上します。1 つのファイルを分割できない場合、他のリーダーがアイドル状態のときに 1 つのリーダーだけがファイルを読み取ることができます。Apache Parquet と Apache ORC は分割可能なファイルもサポートしています。

## 最適化された列指向データストアを使用する
<a name="performance-tuning-s3-throttling-use-optimized-columnar-data-stores"></a>

データを列指向形式に変換すると、Athena クエリのパフォーマンスが大幅に向上します。列指向ファイルを生成する場合、検討すべき最適化手法の 1 つは、パーティションキーに基づいてデータを順序付けることです。

Apache Parquet と Apache ORC は、オープンソースの列指向データストアとしてよく使用されています。既存の Amazon S3 データソースをこれらの形式のいずれかに変換する方法については、「[列形式に変換する](columnar-storage.md#convert-to-columnar)」を参照してください。

### 大きい Parquet ブロックサイズまたは ORC ストライプサイズの使用
<a name="performance-tuning-s3-throttling-use-a-larger-parquet-block-size-or-orc-stripe-size"></a>

Parquet と ORC には、調整して最適化できるデータストレージパラメータがあります。Parquet では、ブロックサイズを最適化できます。ORC では、ストライプサイズを最適化できます。ブロックまたはストライプが大きいほど、それぞれに保存できる行が多くなります。デフォルトでは、Parquet のブロックサイズは 128 MB で、ORC のストライプサイズは 64 MB です。

ORC ストライプが 8 MB (`hive.orc.max_buffer_size` のデフォルト値) 未満の場合、Athena は ORC ストライプ全体を読み取ります。これは、ストライプが小さい場合に、列の選択性と 1 秒あたりの入出力操作の間で Athena が行うトレードオフです。

列数が非常に多いテーブルがある場合、ブロックまたはストライプのサイズが小さいと、必要以上に多くのデータがスキャンされる可能性があります。このような場合は、ブロックサイズを大きくするほうが効率的です。

### 複合型で ORC を使用する
<a name="performance-tuning-s3-throttling-use-orc-for-complex-types"></a>

Parquet に保存されている複雑なデータ型 (例: `array`、`map`、`struct`) の列に対してクエリを実行する場合、現行では、Athena は指定された列のみを選択して読み取るのではなく、データの列全体を読み込みます。これは Athena における既知の問題です。回避策として、ORC の使用を検討してください。

### 圧縮アルゴリズムの選択
<a name="performance-tuning-s3-throttling-choose-a-compression-algorithm"></a>

設定できるもう 1 つのパラメータは、データブロックの圧縮アルゴリズムです。Athena において、Parquet と ORC でサポートされている圧縮アルゴリズムについては、「[Athena 圧縮サポート](https://docs.aws.amazon.com/athena/latest/ug/compression-formats.html)」を参照してください。

Athena での列指向ストレージ形式の最適化の詳細については、AWS ビッグデータのブログ記事「[Amazon Athena のパフォーマンスチューニング Tips トップ 10](https://aws.amazon.com/blogs/big-data/top-10-performance-tuning-tips-for-amazon-athena/)」の「列指向データストア生成の最適化」セクションを参照してください。

## Iceberg テーブル
<a name="performance-tuning-s3-throttling-use-iceberg-tables"></a>

Apache Iceberg は、Amazon S3 で最適に使用できるように設計された、非常に大規模な分析データセット用のオープンテーブル形式です。Iceberg テーブルを使用すると、Amazon S3 のスロットリングを減らすことができます。

Iceberg テーブルには次のような利点があります。
+ Iceberg テーブルは、1 つまたは複数の列でパーティション化できます。これにより、データアクセスが最適化され、クエリでスキャンする必要のあるデータ量が減ります。
+ Iceberg オブジェクトストレージモードでは Iceberg テーブルが Amazon S3 と連携するように最適化されるため、大量のデータや大量のクエリワークロードを処理できます。
+ オブジェクトストレージモードの Iceberg テーブルは、スケーラブルで耐障害性および耐久性があり、スロットリングを減らすのに役立ちます。
+ ACID トランザクションがサポートされているため、複数のユーザーがアトミックな方法で Amazon S3 オブジェクトを追加および削除できます。

Apache Iceberg の詳細については、「[Apache Iceberg](https://iceberg.apache.org/)」を参照してください。Athena で Apache Iceberg テーブルを使用する方法については、「[Iceberg テーブルの使用](https://docs.aws.amazon.com/athena/latest/ug/querying-iceberg.html)」を参照してください。

# クエリを最適化する
<a name="performance-tuning-s3-throttling-optimizing-queries"></a>

このセクションでは、Athena の SQL クエリを最適化する方法を提案しています。

## LIMIT に ORDER BY 句を使用する
<a name="performance-tuning-s3-throttling-use-limit-with-the-order-by-clause"></a>

`ORDER BY` 句は、データをソートされた順序で返します。これには、Athena がすべてのデータ行を 1 つのワーカーノードに送信し、行をソートする必要があります。このタイプのクエリは、長時間実行される場合もあれば、失敗する場合もあります。

クエリの効率を上げるには、上位または下位の *N* 個の値を確認してから、`LIMIT` 句も使用してください。これにより、ソートと制限の両方を単一のワーカーノードではなく個々のワーカーノードにプッシュすることで、ソートのコストが大幅に削減されます。

## JOIN 句の最適化
<a name="performance-tuning-s3-throttling-optimize-join-clauses"></a>

2 つのテーブルを結合すると、Athena は右側のテーブルをワーカーノードに配布し、左側のテーブルをストリーミングして結合を実行します。

このため、結合の左側には大きいテーブルを指定し、結合の右側に小さいテーブルを指定します。これにより、Athena はメモリの使用量を抑え、クエリの実行待ち時間を短縮できます。

また、以下の点に注意してください。
+ 複数の `JOIN` コマンドを使用する場合は、テーブルを最大のものから最小のものまでの範囲で指定してください。
+ クエリで必要でない限り、クロス結合は避けてください。

## GROUP BY 句の最適化
<a name="performance-tuning-s3-throttling-optimize-group-by-clauses"></a>

`GROUP BY` オペレータは、`GROUP BY` 列に基づいて行をワーカーノードに分配します。これらの列はメモリ内で参照され、行が取り込まれると値が比較されます。`GROUP BY` 列が一致すると、値はまとめて集計されます。この処理の仕組みを考慮すると、列のカーディナリティが最も高いものから低いものの順に並べることをお勧めします。

## 文字列の代わりに数値を使用する
<a name="performance-tuning-s3-throttling-use-numbers-instead-of-strings"></a>

数値は文字列に比べてメモリ使用量が少なく、処理も速いため、可能な場合は文字列の代わりに数値を使用してください。

## 列の数を制限する
<a name="performance-tuning-s3-throttling-limit-the-number-of-columns"></a>

データを保存するのに必要なメモリの総量を減らすには、`SELECT` ステートメントで指定する列数を制限してください。

## LIKE の代わりに正規表現を使用する
<a name="performance-tuning-s3-throttling-use-regular-expressions-instead-of-like"></a>

大規模な文字列に `LIKE '%string%'` 句などを含むクエリを使うと、計算量が非常に多くなる可能性があります。文字列の列で複数の値をフィルタリングする場合は、代わりに [regexp\$1like()](https://trino.io/docs/current/functions/regexp.html#regexp_like) 関数や正規表現を使用します。これは、多数の値リストを比較する場合に特に便利です。

## LIMIT 句の使用
<a name="performance-tuning-s3-throttling-use-the-limit-clause"></a>

クエリを実行する際に、すべての列を選択するのではなく、`LIMIT` 句を使用して必要な列のみを返します。これにより、クエリ実行パイプラインで処理されるデータセットのサイズが小さくなります。`LIMIT` 句は、文字列ベースの列数が多いテーブルをクエリする場合に便利です。`LIMIT` 句は、クエリに対して複数の結合や集計を実行するときにも役立ちます。

# 追加リソース
<a name="performance-tuning-additional-resources"></a>

Athena でのパフォーマンスチューニングの詳細については、次のリソースを参照してください。
+ AWS ビッグデータのブログ記事: [Top 10 performance tuning tips for Amazon Athena](https://aws.amazon.com/blogs/big-data/top-10-performance-tuning-tips-for-amazon-athena/)。
+ AWS ビッグデータのブログ記事: AWS ビッグデータのブログの「[Run queries 3x faster with up to 70% cost savings on the latest Amazon Athena engine](https://aws.amazon.com/blogs/big-data/run-queries-3x-faster-with-up-to-70-cost-savings-on-the-latest-amazon-athena-engine/)」。
+ AWS ビッグデータのブログ記事:「[Improve federated queries with predicate pushdown in Amazon Athena](https://aws.amazon.com/blogs/big-data/improve-federated-queries-with-predicate-pushdown-in-amazon-athena/)」。
+ Amazon Simple Storage Service ユーザーガイド:「[設計パターンのベストプラクティス: Amazon S3 のパフォーマンスの最適化](https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance.html)」。
+ [AWS ビッグデータブログの Athena に関する他の記事](https://aws.amazon.com/blogs/big-data/tag/amazon-athena/)。
+ **Amazon Athena** タグを使用して、[AWS re: Post](https://repost.aws/tags/TA78iVOM7gR62_QqDe2-CmiA/amazon-athena) について質問します。
+ [AWS ナレッジセンターの Athena に関するトピック](https://aws.amazon.com/premiumsupport/knowledge-center/#Amazon_Athena)を参照してください。
+ 連絡先 AWS サポート (AWS マネジメントコンソール で、[**サポート**]、[**サポートセンター**] の順にクリック)