RDS
benchmark
lambda

Lambda + RDS benchmark

More than 1 year has passed since last update.

はじめに

LambdaからRDSに接続する時に、Lambdaでは全てのプロセスが独立してしまうので、connectionも使いまわすことが出来ません。
このため、Lambdaに対する同時接続数を増やしていくと、RDS側でconnection数が上限を超えた時にエラーになります。
※ただ、これはLambdaに限らず、Connection Poolingが出来ないシステムでは同じです。

このことにより、Lambda+RDSはアンチパターンであるという見方をすることがあるようです。

結論

  • Lambdaを利用してもRDSのmax connection設定"だけ"が原因でスループットが実用に耐えなくなるということはなさそう。(即アンチパターンというわけではさそう)
  • [追記]RDSがSingleAZの場合、(試験した範囲では)インスタンスタイプを変えることでスループットをスケールすることができる。
  • [追記]RDSがMultiAZの場合、(試験した範囲では)インスタンスタイプに関わらず書き込みスループットは700rps前後を上限に止まってしまう。
  • Lambdaでは外部への通信が発生するケースにおいて、レイテンシを一定時間以下に抑えることは難しそうだ。

RDS for MySQLのmax-connectionについて

RDS for MySQLでは、インスタンスの搭載メモリ毎にmax-connection数が決定されています。
そのため、このconnection数を超えると接続時に接続エラーが発生します。

これは、DB Parameter Groupsのmax_connectionsを確認するとわかるのですが、デフォルトでは、以下の数値となっています。

max_connections: {DBInstanceClassMemory/12582880}
インスタンスタイプ 搭載メモリ max_connections
db.t2.micro 1 GB 85
db.t2.small 2 GB 170
db.t2.midium 4 GB 341
db.t2.large 8 GB 682
db.m4.large 8 GB 682
db.m4.xlarge 16 GB 1365
db.m4.2xlarge 32 GB 2370
db.m4.4xlarge 64 GB 5461

ただし、アプリケーション側の使い方の問題で、メモリ使用量は比較的小さいにも関わらず、同時接続数をあげたいケースには、この制限を変更することが出来ます。(※Apacheの子プロセス数分のconnection poolingを行っている場合など)

max_connections: {DBInstanceClassMemory*2/12582880}

上げすぎるとメモリ不足でエラーとなりますので、落ちない程度に適宜調整して下さい。

Lambda + RDSの構成で負荷試験実施

負荷試験対象API

以下の機能を持つLambda function + API Gatewayを作成しました。

  • 外部APIコール用のtokenをgetの引数として取得
  • 外部のAPIにhttpでのコールを実施して情報取得
  • RDSに上記の結果を格納

インフラ構成

種別 タイプ
Jmeter t2.large
RDS t2.micro

試験実施結果

Jmeter 攻撃スレッド数 Average 95% Line Throughput RDS CPU負荷
50 335 msec 394 msec 146 rps 12%
100 352 msec 416 msec 276 rps 14%

攻撃スレッド数が50スレッドの段階で既に335msecと比較的応答が遅いですが、攻撃スレッド数を2倍にした時にレイテンシがほとんど低下せず、スループットがほぼ2倍にきちんとスケールしています。
RDSのCPUにもまだまだ余裕はありますし、API Gatewayには、デフォルトで1000rpsでスループットの上限がかかってしまいますので、まずはそこまでは上げることが出来るか試してみましょう。

Jmeter 攻撃スレッド数 Average 95% Line Throughput RDS CPU負荷
200 N/A N/A N/A N/A

と思ったのですが、いきなりエラーで落ちてしまいました。
デフォルトでは、API Gatewayの制限ではなく、Lambdaの同時実行数制限の100同時起動までという制約に引っかかってしまうようです。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/limits.html

現在、こちらの上限緩和申請中ですが、「担当部署での確認に数日のお時間を要することがございます。」との事なので続きは気長に待ちたいところです。

応答が遅い原因の調査

スループットとしてはJmeterの攻撃スレッド数を増やすことで上げることはできそうですが、実際問題として、このAPIの応答は非常に遅いことはかわりがありません。それでAPIに少し細工をして、実行結果に実行時の区分ごとの実行時間を含めるようにしました。

{
  "Message": "***",
  ~中略~
  "time": {
    "prepare": "0msec",
    "execute external api": "141msec",
    "db connection": "18msec",
    "db insert": "121msec"
  }
}

npm requestパッケージを利用して外部APIの実行を行っているのですが、その部分だけで実行時間の半分を占めているようです。
実は、この外部APIの実行部分は外部で静的なファイルを返答するのみとしているため、外部への通信の確立のみでかなりのオーバーヘッドはありそうです。

追試

Lambdaの同時実行数制限を1000までに解除してもらったので、追試を行いました。
リファクタリングにより、上記とは少しアプリケーションを変えたため、直接の数値の比較は出来ないことに注意して下さい。

SingleAZ環境における試験

まずは、RDSをSingleAZとして立てて、攻撃スレッド数を上げていくことでスループットを計測していきました。
途中で挙動が不安定になった時点や、レイテンシが遅くなりすぎた時点でインスタンスタイプのスケールアップをして再計測しています。

RDS 攻撃スレッド Average
(msec)
95% Line
(msec)
Throughput
(rps)
特記事項
db.t2.micro 50 218 281 205
db.t2.micro 100 326 330 282 途中からconnectionが不安定
db.t2.micro 200 565 717 331 途中からconnectionが不安定
db.t2.small 50 218 280 207
db.t2.small 100 226 288 379
db.t2.small 200 331 366 547 途中からconnectionが不安定
db.t2.medium 50 210 271 215
db.t2.medium 100 211 272 429
db.t2.medium 200 240 319 745
db.t2.medium 250 252 366 878
db.t2.medium 300 N/A N/A N/A 1000rpsまでのスロットリング
db.t2.large 50 209 268 226
db.t2.large 100 214 275 443
db.t2.large 200 229 307 812
db.t2.large 250 N/A N/A N/A 1000rpsまでのスロットリング

SingleAZ環境においては、インスタンスタイプを選べばスロットリングによる制限の1000rps程度の応答をするAPIは作ることができそうです。

MultiAZ環境における試験

通常、サービスに利用するDBサーバは冗長化のため、MultiAZオプションを利用します。しかし、このMultiAZオプションを利用すると、ゾーン(データセンター)間でのデータの同期を待つことになるため、Write性能がインスタンスのCPUやメモリではなく、ゾーン間のネットワークレイテンシにより制限されてしまう現象が発生することが有ります。
具体的に言うと、より高価なインスタンスを利用しても書き込み性能が増えない可能性があるということです。
実際のサービスを想定して、MultiAZでの負荷試験を行ないます。

RDS 攻撃スレッド Average
(msec)
95% Line
(msec)
Throughput
(rps)
特記事項
db.t2.medium 50 221 281 196
db.t2.medium 100 261 349 350
db.t2.medium 200 303 507 592
db.t2.medium 300 392 899 676 途中からconnectionが不安定
db.t2.medium 400 508 1688 600 途中からconnectionが不安定
db.t2.large 50 249 310 182
db.t2.large 100 248 311 367
db.t2.large 200 269 351 662 ※ピーク時で720rps程度
db.t2.large 300 371 805 722 途中からconnectionが不安定
db.t2.large 400 590 2282 643 途中からconnectionが不安定
db.m4.xlarge 50 229 290 198
db.m4.xlarge 100 236 303 387
db.m4.xlarge 200 267 348 658 ※ピーク時で720rps程度
db.m4.xlarge 300 376 833 717 途中からconnectionが不安定
db.m4.xlarge 400 520 1975 618 途中からconnectionが不安定

SingleAZの場合においてdb.t2.small以上のインスタンスであればスロットリングされるまでの1000rpsまでのスループットが出せたにも関わらず、MultiAZでは、インスタンスタイプを変更しても700rps付近を限界にスループットが上がらないことがわかりました。

念のため、Auroraのdb.t2.mediumインスタンスでも同じ試験をしたのですが、若干数値は良いもののほぼ同じ結果となってしまいました。

※このSingleAZとMultiAZの違いは、Lambdaを利用している事が原因とは限らないことには注意が必要です。