Amazon S3 スロットリングの防止 - Amazon Athena

Amazon S3 スロットリングの防止

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

サービスレベルでのスロットリングを軽減

サービスレベルでの Amazon S3 スロットリングを回避するには、使用状況を監視して Service Quotas を調整するか、パーティション化などの特定の手法を使用します。スロットリングの原因となる可能性があるいくつかの条件は、以下の通りです。

  • [アカウントの API リクエスト制限の超過] — Amazon S3 には、アカウントタイプと使用状況に基づくデフォルトの API リクエスト制限があります。1 つのオブジェクトの 1 秒あたりの最大リクエスト数を超えると、Amazon S3 サービスの過負荷を防ぐためにリクエストが制限されることがあります。

  • [データのパーティション分割が不十分] — データを適切に分割せずに大量のデータを転送すると、Amazon S3 がリクエストを制限する場合があります。パーティション分割の詳細については、このドキュメントの「パーティション分割を使用する」セクションを参照してください。

  • [多数の小さいオブジェクト] — 可能であれば、小さいファイルを多数使用することを避けてください。Amazon S3 には パーティション化されたプレフィックスごとに 1 秒あたり 5,500 件の GET リクエスト という制限があり、Athena のクエリにもこれと同じ制限があります。単一のクエリで何百万もの小さなオブジェクトをスキャンする必要がある場合は、クエリが Amazon S3 によって制限される可能性が高くなります。

過度のスキャンを避けるには、AWS Glue ETL を使用してファイルを定期的に圧縮する、またはテーブルをパーティション化してパーティションキーフィルターを追加します。詳細については、以下のリソースを参照してください。

テーブルの最適化

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

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

パーティション分割を使用する

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

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

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

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

Athena でのパーティション分割の詳細については、「パーティション化とは」を参照してください。

データのバケット化

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

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

Athena でのバケット化の詳細については、「バケット化とは」を参照してください。

AWS Glue パーティションインデックスの使用

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

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

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

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

AWS Glue でのパーティションインデックス作成のステップについては、「AWS Glue デベロッパーガイド」の「AWS Glue でパーティションインデックスを使用する」を参照してください。テーブルプロパティを追加してパーティションフィルタリングを有効にする方法については、「AWS Glue パーティションのインデックス作成とフィルタリング」を参照してください。

データ圧縮とファイル分割の使用

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

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

最適化された列指向データストアを使用する

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

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

大きい Parquet ブロックサイズまたは ORC ストライプサイズの使用

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

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

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

複合型で ORC を使用する

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

圧縮アルゴリズムの選択

設定できるもう 1 つのパラメータは、データブロックの圧縮アルゴリズムです。Athena において、Parquet と ORC でサポートされている圧縮アルゴリズムについては、「Athena 圧縮サポート」を参照してください。

Athena での列指向ストレージ形式の最適化の詳細については、AWS ビッグデータのブログ記事「Amazon Athena のパフォーマンスチューニング Tips トップ 10」の「列指向データストア生成の最適化」セクションを参照してください。

Iceberg テーブル

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

Iceberg テーブルには次のような利点があります。

  • Iceberg テーブルは、1 つまたは複数の列でパーティション化できます。これにより、データアクセスが最適化され、クエリでスキャンする必要のあるデータ量が減ります。

  • Iceberg オブジェクトストレージモードでは Iceberg テーブルが Amazon S3 と連携するように最適化されるため、大量のデータや大量のクエリワークロードを処理できます。

  • オブジェクトストレージモードの Iceberg テーブルは、スケーラブルで耐障害性および耐久性があり、スロットリングを減らすのに役立ちます。

  • ACID トランザクションがサポートされているため、複数のユーザーがアトミックな方法で Amazon S3 オブジェクトを追加および削除できます。

Apache Iceberg の詳細については、「Apache Iceberg」を参照してください。Athena で Apache Iceberg テーブルを使用する方法については、「Iceberg テーブルの使用」を参照してください。

クエリの最適化

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

LIMIT に ORDER BY 句を使用する

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

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

JOIN 句の最適化

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

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

また、以下の点に注意してください。

  • 複数の JOIN コマンドを使用する場合は、テーブルを最大のものから最小のものまでの範囲で指定してください。

  • クエリで必要でない限り、クロス結合は避けてください。

GROUP BY 句の最適化

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

文字列の代わりに数値を使用する

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

列の数を制限する

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

LIKE の代わりに正規表現を使用する

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

LIMIT 句の使用

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

追加リソース

設計パターンのベストプラクティス: Amazon S3 のパフォーマンスの最適化 (Amazon Simple Storage Service ユーザーガイド) を参考にしてください。

Athena でのパフォーマンスのチューニング