RDS Proxyを使うとDB接続処理は早くなるのか?

2020.07.07

CX事業本部@大阪の岩田です。

コネクションプーリングのメリットとして、接続済みのDB接続をプーリングして再利用することでアプリケーションからDBに接続する際のオーバーヘッドが削減できる というメリットがあります。このメリットはアプリケーションレイヤでDB接続をプーリングするアーキテクチャにおいては効果が大きいですが、RDS Proxyのようなプロキシ型のコネクションプーリングでは効果が薄くなりがちです。アプリケーションからDBプロキシに接続する際にTCPの3WAYハンドシェイク等の接続処理が必要になるからです。

このブログではLambdaからRDS/RDS Proxyに対して接続/切断を繰り返し、RDS Proxyを利用することでDB接続処理が早くなるか?を実際に検証してみました。

計測方法と環境(共通)

ざっくり以下の手順で計測しました。

  • メモリを1792MB割り当てたLambda(Python3.7)からRDS/RDS Proxyに接続/切断し、所要時間を計測
  • Lambdaは連続で100回直列実行し、結果をCloudWatch Logs Insightsで集計
  • LambdaからはSQLを発行せずに、あくまでLambda <-> RDS Proxyの接続速度だけを評価する
    • SQLを発行するとRDS Proxyを経由してRDSとのやりとりが発生するためオーバーヘッドが発生します
    • RDS側のプロセス/スレッド生成処理が省略されることでパフォーマンスが改善されるか?といった観点で評価します
  • RDSはシングルAZ構成(※今回の検証ではSQLを発行していないのでRDSのAZ構成についてはあまり関係ないはず)
  • LambdaもシングルAZ構成とし、RDSと同じAZのサブネットを設定
  • RDS ProxyはシングルAZ構成が取れないため、マルチAZ構成
  • DBの認証はIAM認証ではなく、通常のユーザーID&パスワードによる認証を利用
  • RDS Proxyが事前にRDSとの接続を確立している状態で計測するため、事前準備としてEC2からRDS Proxyに接続&切断しておきRDS Proxyに接続をプールしておく(※SQLを発行していないのでこれも計測結果に関係ないはず)

CloudWatch Logs Insightsのクエリは以下のクエリを使用しました。

filter (@type = "REPORT" and @message not like /Init Duration/)
| stats avg(@duration), max(@duration), min(@duration),pct(@duration, 90), pct(@duration, 50), count(@duration)

@message not like /Init Duration/の部分でコールドスタートのログは除外するようにしています。

RDS for MySQLの場合

DBエンジンにRDS for MySQL(非Aurora)を選択した場合の計測パターンです。

環境

  • バージョン:MySQL 5.7.30
  • インスタンスタイプ:db.m5.large
  • ストレージタイプ:汎用(SSD) 20GB
  • DBパラメータグループ:default.postgres-11

ソースコード

これだけです

import json
import pymysql

def lambda_handler(event, context):

    conn = pymysql.connect(<RDS/RDS Proxyのエンドポイント>, user=<ユーザー名>, passwd=<パスワード>, db=<DB名>)
    conn.close()
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

RDS Proxy無しの場合

RDS Proxy無しで直接接続した場合の結果は以下の通りです。

avg(@duration) max(@duration) min(@duration) pct(@duration, 90) pct(@duration, 50) count(@duration)
16.6381 296.4 3.9 38.45 4.86 100

平均16.6ms、中央値だと4.8msとかなり高速な印象です。

RDS Proxy有りの場合

RDS Proxyを使うと以下のような計測結果になりました

avg(@duration) max(@duration) min(@duration) pct(@duration, 90) pct(@duration, 50) count(@duration)
47.3048 459.06 23.23 58.42 34.12 100

RDS Proxy無しの時より少しパフォーマンスが悪化しています。

RDS for PostgresSQLの場合

DBエンジンにRDS for PostgresSQL(非Aurora)を選択した場合の計測パターンです。PostgreSQLは接続確立時にプロセスのforkが発生するので、比較的接続コストが大きくなりがちです。MySQLよりもコネクションプーリングによる接続速度向上の恩恵を受けやすそうです。

環境

  • バージョン:PostgreSQL 11.8-R1
  • インスタンスタイプ:db.m5.large
  • ストレージタイプ:汎用(SSD) 20GB
  • DBパラメータグループ:default.postgres-11

ソースコード

これだけです

import psycopg2
import json

def lambda_handler(event, context):

    conn = psycopg2.connect(host=<RDS/RDS Proxyのエンドポイント>, database=<DB名>, user=<ユーザー名>,password=<パスワード>)
    conn.close()

    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

RDS Proxy無しの場合

RDS Proxy無しで直接接続した場合の結果は以下の通りです。

avg(@duration) max(@duration) min(@duration) pct(@duration, 90) pct(@duration, 50) count(@duration)
23.0851 286.06 8.85 40.34 10.64 100

RDS Proxy有りの場合

RDS Proxyを使うと以下のような計測結果になりました

avg(@duration) max(@duration) min(@duration) pct(@duration, 90) pct(@duration, 50) count(@duration)
94.5185 632.84 24.3 175.72 54.93 99

PostgreSQLの場合もRDS Proxy無しの時よりパフォーマンスが悪化しちゃいましたね...

まとめ

4パターンをまとめると、以下のような結果になりました

計測パターン 平均値 最大値 最小値 90パーセンタイル 中央値
MySQL直接接続 16.6381 296.4 3.9 38.45 4.86
MySQL & RDS Proxy 47.3048 459.06 23.23 58.42 34.12
MySQL直接接続 23.0851 286.06 8.85 40.34 10.64
PostgreSQL & RDS Proxy 94.5185 632.84 24.3 175.72 54.93

今回利用した計測方法だと、MySQL,PostgreSQLいずれの場合もRDS Proxyを使用した方がアプリケーションからDBに接続する速度は遅いという結果に終わりました。RDS ProxyはシングルAZ構成が取れないためマルチAZで構築していますが、Lambda(AZ-A) -> RdS Proxy(AZ-C)のようなAZ跨ぎの接続が発生していたとかでしょうか? また改めてマルチAZ構成のパターンでも検証してみたいと思います。

今回の結果からも分かりますが、思考停止的に「コネクションプーリング使うとDB接続が早くなるらしい。」→ 「RDS Proxyを導入しよう」と考えるのは危険です。自身のユースケースに沿った形で計測/評価してから導入を検討しましょう。また、今回は接続処理の速度のみを取り上げましたが、どちらかというとRDS ProxyのメリットはDB接続を複数のクライアントで共有することによる可用性の向上にあると思います。接続速度以外にも色々な評価軸からRDS Proxyの導入有無について検討するようにしましょう。