DynamoDB Streamsのバッチサイズについて気をつけること
はい、どーも。モバイルアプリサービス部の吉田です。
モバイルアプリケーション開発する際、アプリからのデータ保存先として、DynamoDBはよく利用されます。ただそのままでは集計処理などをおこなうには不向きですので、DynamoDB StreamsをトリガにLambdaを使用してRDSなどに書き込むといったアーキテクチャも、よく利用されると思います。
このとき気をつけておいたほうがいいことを、挙げてみます。
バッチサイズは最適な値にしよう
上記の構成でRDSへの書き込みのスループットを決める要素として大事なものに、Lambdaのバッチサイズがあります。バッチサイズは1回のLambda呼び出しが受け取ることができる最大のレコード数です。これが少ない数だと1回のLambdaで処理できる件数が少なくなり、結果としてスループットが悪くなります。ですのでDynamoDB Streams + Lambdaの構成では、バッチサイズを最適な値に設定することは、システムを安定的に稼働させるためには不可欠です。
バッチサイズがコンソールからは変更できませんが、CLIであれば変更可能です。まずは、以下のコマンドで対象のLambdaのUUIDを取得します。
$ aws lambda list-event-source-mappings --function-name [ファンクション名]
上記コマンドで返ってくる内容に、対象のLambdaのUUIDが含まれます。またこれには現在のバッチサイズなども含まれていますので、正しく変更されたかどうかの確認にも使うことができます。
対象のUUIDを取得したら、以下のコマンドでバッチサイズを変更できます。
$ aws lambda update-event-source-mapping --uuid [対象のLambdaのUUID] --batch-size [変更後のバッチサイズ]
変更されたかどうかの確認はCLIのlist-event-source-mappingsから行うか、コンソールからでも行なえます。
データ投入時にはトリガを無効化して投入しよう
バッチサイズを変更したときは、負荷テストを行います。何らかの方法でDynamoDBに大量のデータを登録し、処理が終了するまでにどの程度の時間がかかっているのかをチェックすることで、スループットを計測できます。自分は以下のようなRubyのスクリプトでデータを投入しました。
require 'aws-sdk' dynamoClient = Aws::DynamoDB::Client.new({ access_key_id: [アクセスキー], secret_access_key: [シークレットアクセスキー] }) 500.times do |index| dynamoClient.put_item( table_name: [DynamoDBのテーブル名], item: { ... } ) end
しかし上記のようにしてデータを投入しても、1回のLambdaで10件程度のレコードしか処理されませんでした。これはデータの投入自体が遅いことが原因です。
このようなときには
- トリガを、一旦無効化する
- スクリプトなどで大量のデータを投入する
- トリガを、再度有効化する
という手順でデータを投入することで、Lambdaがバッチサイズで設定した値にほぼ近い件数のレコードを処理することができます。具体的には以下のように
aws lambda update-event-source-mapping --uuid [対象のLambdaのUUID] --no-enabled
として、一旦DynamoDB Streamsを止めてデータを投入します。その後
aws lambda update-event-source-mapping --uuid [対象のLambdaのUUID] --enabled
としてトリガを有効にします。こうすることで、意図したテストを実行できるようになります。
実際のLambdaの実行時間については、CloudWatchのログからDurationの値を見るのが良いと思います。
REPORT RequestId: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXX Duration: 46.64 ms Billed Duration: 100 ms Memory Size: 512 MB Max Memory Used: 25 MB
Lambda内部で処理件数をログに出力すれば、1回のLambdaで毎秒どの程度の件数を書き込めているか、計算できますね。
以上、DynamoDB Streamsのバッチサイズについて気をつけること、でした