DynamoDBで運用しているテーブルにAthenaでクエリを実行するために考えたこと

2021.02.08

最初に

こんにちは、yoshimです。

AthenaからDynamoDBで運用しているテーブルにアクセスする、となると一番シンプルなのはAthenaのFederated Queryを使う事だと思います。
ただ、他にも「DynamoDBテーブルのデータをS3に出力→AthenaでこのS3のデータを参照」といった方法もあり、これらの方法の使い分けのイメージが自分の中で整理できていなかったので、それぞれの手法を実際に手を動かして確認してみました。

なお、本エントリーでは「各サービスの説明」や「具体的な手順」についての説明は必要最低限のみ、とします。

目次

1.はじめに

本エントリーではAthenaからDynamoDBで運用しているテーブルにSQLを実行するために、下記2パターンの構成について比較していきます。
今回利用するDynamoDBテーブルは、スクリプトで適当にサンプルデータを投入したものです。
合計103,578行、8列のテーブルデータです。

パターン1.AthenaのFederated QueryでDynamoDBテーブルにアクセスする

Athenaの「フェデレーテッドクエリ」機能でコネクタ(Lambda関数)を介して、DynamoDBにアクセスする方法です。
この時、1回のクエリでLambda関数が複数実行されたり、一時領域としてS3を利用したりします。
コネクターの挙動について気になる方はこちらをご参照ください。

参照:Amazon Athenaの新しいフェデレーテッド・クエリによる複数データソースの検索

パターン2.DynamoDBテーブルをS3にファイル出力して、そのファイルにAthenaからアクセスする

このパターンでは、バッチジョブを用意して定期的に参照対象データを更新します。
AWS上の構成としては下記のような形になります。

AthenaからはGlueのテーブルではなく、テーブルを利用したViewを参照させます。
バッチジョブで「新規テーブルを作成」&「その新規テーブルを参照するようにView定義を更新」を定期的に実行します。

2.パターン1の検証

「パターン1.AthenaのFederated QueryでDynamoDBテーブルにアクセスする」の検証です。
本エントリーでは手順の紹介はしませんが、気になる方はこちらが参考になると思います。

さて、実際にAthenaからアクセスした際の挙動についてですが、こちらに概要が書いてあります。

Performance
The Athena DynamoDB Connector does support parallel scans and will attempt to push down predicates as part of its DynamoDB queries. A hash key predicate with X distinct values will result in X Query calls to DynamoDB. All other predicate scenarios will results in Y number of Scan calls where Y is heuristically determined based on the size of your table and its provisioned throughput.

ハッシュキーを指定していたらそのキーを使ってクエリをする、そうでないならスキャンが実行されるようです。
スキャンはDynamoDBテーブルのリードキャパシティへの影響が怖いですね...。
とはいえ、SQLを書く際にハッシュキーを常に意識するのも現実的では無いですね。
「キャパシティモードをオンデマンドモードにする」、という手も思い浮かびますが、「テーブルデータの更新要件によっては、プロビジョニング済の方が割安」なので「必要以上にコストが高くつく」ケースがあったり、オンデマンドモードでも対応しきれないスパイクによりキャパシティが枯渇してしまうリスクがあります。

他にも、1回のクエリで複数のLambdaが実行されるので、利用しすぎるとLambdaの同時実行数のクオータに引っかかる可能性が出てくる、といったところも一応考慮しておいてもいいかもしれません。

考察はここまでとして、実際に触ってみて挙動を確認しました。
今回検証したケースは下記4ケースです。

ケース 検証結果
テーブルのPKでアクセス リードキャパシティを殆ど消費せずに参照できる
GSIのPKでアクセス カーディナリティが低いアトリビュートなので、それなりにキャパシティを消費する
GSIのPK&SKでアクセス リードキャパシティを殆ど消費せずに参照できる
PKを指定しないでアクセス スキャンされるので、リードキャパシティを消費する

「Athenaでスキャンしたデータ量」と「DynamoDBのリードキャパシティ消費量」の2点を確認しつつ、検証を進めました。
それぞれのケースごとの画像を添付しておきます。

テーブルのPKでアクセス

PKには一意になる値を入れています。
なのでこのケースではキャパシティを殆ど消費することなくクエリを実行できました。

DynamoDB側のリードキャパシティも殆ど消費されていないことがわかります。
(関係ない時間にいくつかクエリ発行していますが、本クエリ実行時の16:39のキャパシティ利用がない事を確認)

GSIのPKでアクセス

値が3種類しかないアトリビュートをPKに指定してアクセスしてみました。
想定どおり、「クエリで参照(Not スキャン)」できているものの、抽出されるアイテムが多いのでリードキャパシティをそれなりに消費しています。

DynamoDBのキャパシティもそれなりに消費しているものの、後述するスキャン時と比較するとちゃんとPKを指定している分消費するキャパシティ量は少なめです。

GSIのPK&SKでアクセス

このGSIは、「PKとSKの両方を指定することで一意になる」ように作っているので、「テーブルのPKでアクセス」と同じ結果になると想定していました。
実際に、そのような挙動が確認できました。

キャパシティを消費しているのは、前述の「GSIのPKでアクセス」での検証によるものです。
「GSIのPK&SKでアクセス」は16:46に実行し、その時間は殆どキャパシティを消費していないことがわかります。

PKを指定しないでアクセス

PKは指定せず、適当なアトリビュート(uuidを投入しているアトリビュート)をWhere句で指定してみました。
Athenaのスキャンデータ量は少ないものの、DynamoDBテーブルにはスキャンを実行するため、キャパシティをかなり消費しています。

画像がイマイチですが、キャパシティで言うと40程度、スキャンした時と同じだけキャパシティを消費していました。

というわけでいくつかの懸念点はあるものの、「AWS上の構成がシンプルで済むこと」、「ほぼほぼ最新のデータ(結果整合性)を参照できる」点はやはり大きなメリットです。
以上から、「パターン1.AthenaのFederated QueryでDynamoDBテーブルにアクセスする」のメリットデメリットを下記のようにまとめました。

メリット

  • 構成がシンプル
  • 最新のデータを利用できる
  • Indexを複数利用できる

デメリット

  • DynamoDBのキャパシティへの影響
    • DynamoDBのテーブルにスキャンが実行される可能性があるため、キャパシティが枯渇する可能性がある
    • オンデマンドモードにする、という対応方法もある
      • しかし、キャパシティを予約している場合よりも、コストが割高になる可能性がある
      • オンデマンドモードでも対応しきれないスパイクを発生させてしまう可能性が残っている
        • 詳しくはこちらの「ピークトラフィックとスケーリングプロパティ」を確認してください
  • 利用者が増えすぎた場合はLambdaの同時実行数クオータに影響があるかも
    • 上限緩和申請をすればいい、というものではありますがそれが間に合わなかった場合にLambdaが起動できずに問題になりうる

3.パターン2の検証

「パターン2.DynamoDBテーブルをS3にファイル出力して、そのファイルにAthenaからアクセスする」の検証です。
このパターンだと、Athenaで参照できるデータ鮮度が「JOBがどれだけすぐに終わるか」に大きく左右されるため「JOBの実行時間」を検証します。

今回、DynamoDBはオンデマンドモード、GlueJobのread.percentはデフォルト値(0.5)のまま、GlueJobの読み取り並列度は1で確認しました。

さて、今回用意したサンプルテーブル(10万行/8列)の場合だと、JOBの実行時間は下記の通りでした。
ユースケースにもよりますが、まあそこそこの実行時間で完結します。
JOBの実行時間はテーブルサイズや列数等、様々な要因で変動しますので、正確な値を見積もりたい場合は実際にやってみるのがいいと思います。

実行時間(sec)
1回目 251
2回目 264
3回目 258
4回目 238
5回目 178
平均 209

さて、「パターン2.DynamoDBテーブルをS3にファイル出力して、そのファイルにAthenaからアクセスする」についても、メリットデメリットをまとめます。

メリット

  • Federated Queryに比べて、コストが削減しやすい
    • DynamoDBのリードキャパシティへの影響を管理しやすいので、プロビジョニング済みキャパシティーモードで運用している場合でも対応しやすい
  • DynamoDBのキャパシティへの影響も管理しやすい

デメリット

  • バッチジョブを管理する必要がある
  • 参照できるデータの鮮度は、バッチジョブの実行タイミングや実行時間に左右される

4.まとめと比較

それぞれのパターンのメリットデメリットを再掲します。

パターン メリット デメリット
パターン1.AthenaのFederated QueryでDynamoDBテーブルにアクセスする - 構成がシンプル
- 最新のデータを利用できる
- Indexを複数利用できる
- DynamoDBのキャパシティへの影響を考慮する必要がある
- 利用者が増えすぎた場合はLambdaの同時実行数クオータに影響があるかも
パターン2.DynamoDBテーブルをS3にファイル出力して、そのファイルにAthenaからアクセスする - Federated Queryに比べて、コストが削減しやすい
- DynamoDBのキャパシティへの影響も管理しやすい
- バッチジョブを管理する必要がある
- 参照できるデータの鮮度は、バッチジョブの実行タイミングや実行時間に左右される

それぞれメリットデメリットがありますね。
なんとなく、それぞれのケースの使い分けがイメージできました。

「パターン1.AthenaのFederated QueryでDynamoDBテーブルにアクセスする」はシンプルで便利だけど、使い過ぎるとリスクがあるかもしれません。
それに比べると「パターン2.DynamoDBテーブルをS3にファイル出力して、そのファイルにAthenaからアクセスする」は色々と管理しやすい一方で、「データの鮮度」はJOBに大きく左右されてしまいます。

個人的には後者の方が好きですが、実際に採用する際は「ユースケース/AWSリソースのキャパシティ(DynamoDB/Lambda)/コスト/管理体制」等に合わせて考慮する必要がありそうです。

5.参照