なぜAWS LambdaとRDBMSの相性が悪いかを簡単に説明する

2020/01/20 Update: 

本エントリの内容は2019年12月3日にアナウンスされた『Amazon RDS Proxy』のリリースにより完全に陳腐化しました。過去のアンチパターンがフィードバックをもとにした改善によってアンチパターンではなくなるという最高の事例です。

サーバーレス元年始まった!

 

RDS Proxyの詳細はこちらからどうぞ。まだプレビューですがぜひ試してみてください。

aws.amazon.com

※ 記録のために元のエントリは残しておきますが、もはや参考にはならないのでご注意ください。

 

f:id:Keisuke69:20170621131534j:plain

表題の通りです。
AWS LambdaとRDBMS全般(Amazon RDSに限らず)はその特性的に相性が悪いのでデータストアはなるべくAmazon DynamoDBを使いましょうという話しをよくします。
今回はなぜそうなのかをもう少しだけ説明しようと思います。

大前提

まず、大前提としてAWS Lambdaは水平方向へのスケーラビリティを特徴とするサービスです。リクエストが来たらそれを処理するLambdaファンクションのコンテナを作成し、処理を実行します。

このLambdaファンクションのコンテナは1つあたり同時に1リクエスト(1イベント)しか処理しません。従って同時に100リクエスト来た場合は同じLambdaファンクションのコンテナ100個で処理されます。ただし、必ずしも毎回100コンテナが1から作成されて起動されるわけではありません。

Lambdaファンクションのコンテナは起動コストの効率化のために再利用も行います。いわゆるウォームスタートというものですね。ウォームスタート可能なコンテナがあれば新しくコンテナを作ることはせずにそれを利用します。しかし、ウォームスタートできるコンテナがない場合やあってもその数以上のリクエストを同時に処理する必要が発生した場合は新しくコンテナが作成・起動されます。これがデフォルトの同時実行1000だと1,000コンテナが同時に実行される可能性があるわけです。もちろん10,000であれば10,000個です。とはいえ、AWS Lambdaを中心とするサーバレスのプラットフォームはNever Pay for Idleなのでリクエストがなければ一切費用は発生しません。

最大同時接続数の問題

ご存知の通り各種RDBMSには最大同時接続数的なパラメータがあるわけですが、設定可能な値と実際に使い物になる値は別物です。DBエンジンごとの多少の違いはもちろんあるものの、使い物になる数値というのはCPU数やメモリ数と大きく関係してきます。

当然ながら同時に接続される数が多ければ多いほど必要なCPU数、メモリ数が増えていきます。つまり、アクティブな接続が同時に数千から数万になるDBを動かすにはそれなりのスペックが必要というわけです。そもそもプロセスやスレッドが数千とか数万になってくると1サーバのOSだとコンテキストスイッチやら何やら厳しくなってくるので、DB分割なんかも検討するかと思います。

また、RDBMSというと、現状ではほとんどが処理量が増えた場合にスケールアウトで対応するのではなくスケールアップが必要になります。要はサーバに積むCPU、メモリなどを増やす方向です。参照系はスケールアウトできても更新系はスケールアップで対応するしかないことが多いです。

さて、ポイントは先のLambdaファンクションの振る舞いとの兼ね合いです。まず、説明のとおり、リクエストがあるとコンテナが作られて起動されるのでこのタイミングでDBへのコネクションが張るわけですが、リクエストのたびに接続するのはコストがかかるので、その接続オーバーヘッドを減らすための方法として、グローバルスコープを利用することでコンテナがアクティブな間、つまりウォームスタートの場合はそれを利用するという方法があります。

一方で先ほどの説明の通り、Lambdaは同時に処理すべきリクエストは必要なリクエスト数分のコンテナで処理します。つまり、各コンテナからDBへとそれぞれコネクションが張られることになります。ということは仮にデフォルト状態であっても1000コネクションが張られる可能性があるということです。これがプロダクション環境でもう少し大きいシステムであれば数千同時実行 = 数千コネクションとかにになってきます。こうなってくると一般的にはコネクションプールを使うことが多いと思います。DBへ都度接続するコストを減らした上で、コネクションを使いまわすことで負荷を減らすわけです。

ところが、Lambdaではこのコネクションプールを実現することが難しいのです。Lambdaファンクションのコンテナ間では何も共有しませんし、外部のデータストアへと永続化しない限りはできないからです。つまり、コンテナをまたいで利用するようなコネクションプールの実装は難しいと言えます。それを管理する中間層のようなものを用意して各コンテナはそれ経由でDBにアクセスするような何かを作るとかして頑張ればなんとかなるかも知れませんが、ちゃんと検討したことないのでわかりません。

話しを戻すと、このようなLambdaファンクションのプラットフォーム特性を考えるとダウンストリームとなるシステムも同様にスケールアウトするようなものでないと耐えきれないです。同時実行数が少ないうちはいいですが増えてくると耐えられなくなってきます。実はこれはRDBMSに限らない話しなんですけどね。スケールアウトできないシステムの場合、ある程度まではピークにあわせたスペックのサーバを用意しておくことで対応できるとは思いますが、限界は来るかと思います。また、せっかくNever pay for idleを特徴とするプラットフォームを利用するにも関わらず、ピークのために通常よりも巨大なスペックのDBサーバを維持するのももったいない話しですしね。

というわけでLambdaと組み合わせて利用するデータストアとしてはDynamoDBを推奨しているわけです。DynamoDBの場合は分散型データベースであり同時接続数の増加そのものは気にする必要がなくなります。また、一貫して高速で安定したパフォーマンスを得られます。もちろんスループット増に伴ってコストは発生しますが、そもそも対応できないという状況はなくなります。

VPCアクセスのレイテンシコスト問題

こちらについては改善されることが発表されました。2019年9月以降に順次適用される予定です。

aws.amazon.com

これはRDBMSに限らない話しなのですがLambdaファンクションがVPC内のリソースにアクセスしようとしたとき、かつコールドスタートが発生したときはその仕組み上10秒〜30秒ほどのレイテンシが上乗せされます。いくらコールドスタートの発生頻度が低いとはいえ、ゼロにすることはできないです。

一方で、RDBMSVPCの中に置くことが多いと思います。そうするとこのレイテンシの考慮が必要になってきます。ここはシステムの要件次第なので一概には言えないですが、DBアクセスを伴う処理がたまにとは言え10秒を超えてくることを許容できるかどうかです。

まとめ

LambdaとRDBMSの組み合わせをあまり推奨しない理由を2つ挙げました。
もちろんそれほど同時実行されることがないのであれば最初の問題はほとんど関係ないかも知れません。また、たまに発生する10秒を超える実行時間が問題ない場合もあるでしょう。

逆に言うとそうでない限り、LambdaとRDBMSを組み合わせて利用するのは止めたほうがいいです。すべてのLambdaファンクションがRDBMSに接続する必要がないのであればDynamoDBも併用してRDBMSの利用は必要最低限にするのもありですし、処理によってはLambdaファンクションのアクセス先はあくまでもDynamoDBにしてストリームを使って非同期にRDBMSに反映するという方法が取れるかも知れません。

このあたりはケースバイケースなので必要であればご相談ください。

 

最後にしつこいくらいに自分の本を宣伝しておきますね。

実践AWS Lambda ~「サーバレス」を実現する新しいアプリケーションのプラットフォーム~

実践AWS Lambda ~「サーバレス」を実現する新しいアプリケーションのプラットフォーム~

 

  

Amazon Web Servicesを使ったサーバーレスアプリケーション開発ガイド

Amazon Web Servicesを使ったサーバーレスアプリケーション開発ガイド

 

 

©Keisuke Nishitani, 2023   プライバシーポリシー