Edited at

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を利用している事が原因とは限らないことには注意が必要です。