Aurora DSQL の書き込み・読み取り性能を AWS Lambda から検証してみた
AWS の新しいサーバーレス分散データベースである Aurora DSQL。実際のワークロードでどのような性能特性を示すのかを把握するため、AWS Lambda を用いて書き込み・読み取り性能を計測しました。
本記事では、並列数やリージョン間の物理的距離(クロスリージョン)による影響や、DB コネクション再利用の有無による性能差を検証しています。さらに、シングルリージョンとマルチリージョン構成における書き込み性能のトレードオフの確認も試みました。
1. 検証環境
| 項目 | 値 |
|---|---|
| Lambda ランタイム | Node.js 22.x / ARM64 (Graviton) |
| Lambda メモリ | 1,769MB(1 vCPU 相当) |
| Lambda リージョン | ap-northeast-1(東京) |
| DSQL クラスタ | ap-northeast-1(東京)/ us-west-2(オレゴン)各シングルリージョン、東京+ソウル マルチリージョン(witness: 大阪) |
| 並列実行基盤 | Step Functions Map ステート(MaxConcurrency で並列数制御) |
| コネクタ | @aws/aurora-dsql-postgresjs-connector |
テストテーブルは以下の定義です。
CREATE TABLE perf_test (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
data TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);
2. IAM ロールを用いた最小権限設計
Step Functions のロールは Lambda の呼び出し権限のみ、Lambda のロールは DSQL への接続権限のみを持ちます。
Lambda 実行ロールのポリシー
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "dsql:DbConnect",
"Resource": [
"arn:aws:dsql:ap-northeast-1:<account-id>:cluster/<cluster-id-tokyo>",
"arn:aws:dsql:us-west-2:<account-id>:cluster/<cluster-id-oregon>"
]
}
]
}
dsql:DbConnect は一般ユーザーとしての接続を許可するアクションです。Resource を特定クラスタの ARN に限定することで、他のクラスタへの接続も防ぎます。
DB ロールと IAM の紐付け
-- admin で接続して実行
CREATE ROLE bench_user WITH LOGIN;
AWS IAM GRANT bench_user TO 'arn:aws:iam::<account-id>:role/<lambda-role-name>';
GRANT USAGE ON SCHEMA public TO bench_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO bench_user;
AWS IAM GRANT は DSQL 固有の構文で、IAM ロール ARN と DB ロールを紐付けます。この設定はクラスタごとに必要です(マルチリージョン構成では自動伝播します)。GRANT ... ON ALL TABLES は実行時点で存在するテーブルにのみ有効です。DSQL は ALTER DEFAULT PRIVILEGES をサポートしていないため、テーブルを追加した場合は再度 GRANT が必要でした。
3. Lambda 関数の実装
import { auroraDSQLPostgres } from "@aws/aurora-dsql-postgresjs-connector";
const ENDPOINTS = {
"ap-northeast-1": process.env.DSQL_ENDPOINT_TYO,
"us-west-2": process.env.DSQL_ENDPOINT_PDX,
};
let cachedSql = null;
let cachedRegion = null;
function getSql(region) {
if (cachedSql && cachedRegion === region) return cachedSql;
cachedSql = auroraDSQLPostgres({
host: ENDPOINTS[region],
username: "bench_user",
});
cachedRegion = region;
return cachedSql;
}
export const handler = async (event) => {
const op = event.op || "insert";
const region = event.region || "ap-northeast-1";
const cache = event.cache !== false;
const start = Date.now();
const sql = cache
? getSql(region)
: auroraDSQLPostgres({ host: ENDPOINTS[region], username: "bench_user" });
try {
if (op === "insert") {
const id = crypto.randomUUID();
await sql`INSERT INTO perf_test (id, data) VALUES (${id}, ${"bench"})`;
} else if (op === "select") {
await sql`SELECT id, data FROM perf_test LIMIT 1`;
}
const ms = Date.now() - start;
if (!cache) await sql.end();
return { ok: true, op, region, cache, ms };
} catch (e) {
const ms = Date.now() - start;
if (!cache) try { await sql.end(); } catch {}
return { ok: false, op, region, cache, ms, error: e.message?.slice(0, 200) };
}
};
cachedSql をグローバル変数に保持し、ウォームスタート時にコネクションを再利用します。公式コネクタが IAM 認証トークンの生成・リフレッシュを透過的に行うため、コネクション再利用時のトークン期限管理は不要です。
比較のため、event.cache に false を指定すると都度接続となる実装も用意しました。
4. コネクション再利用の効果
並列度 1
再利用なし(都度 DB 接続)と再利用あり(DB コネクション保持)の比較です。
| パターン | 再利用なし | 再利用あり |
|---|---|---|
| 東京→東京 INSERT | 222ms | 21ms |
| 東京→東京 SELECT | — | 6ms |
| 東京→オレゴン INSERT | 1,512ms | 106ms |
| 東京→オレゴン SELECT | — | 102ms |
再利用なしでは TCP/TLS ハンドシェイクと IAM トークン生成が毎回発生します。特にクロスリージョンでは RTT が大きいためハンドシェイクの往復回数分だけ影響が増幅され、東京→オレゴンの INSERT は 1,512ms かかっています。再利用ありでは東京→東京 INSERT が 21ms、オレゴンでも 102〜106ms まで短縮され、純粋な RTT とほぼ一致しています。
並列実行(再利用なし)
Step Functions の Map ステートで Lambda を並列実行し、各 Lambda が 1 回ずつ DSQL に接続・操作する構成です。
東京→東京
| 並列度 | op | med(ms) | p95(ms) | rps |
|---|---|---|---|---|
| 50 | INSERT | 162 | 612 | 28.3 |
| 100 | INSERT | 105 | 518 | 44.7 |
| 200 | INSERT | 95 | 263 | 48.7 |
| 50 | SELECT | 88 | 118 | 53.8 |
| 100 | SELECT | 88 | 143 | 48.8 |
| 200 | SELECT | 90 | 133 | 53.4 |
再利用なしでは rps が 50 前後で頭打ちになります。
東京→オレゴン
| 並列度 | op | med(ms) | p95(ms) | rps |
|---|---|---|---|---|
| 50 | INSERT | 1,338 | 1,473 | 21.9 |
| 100 | INSERT | 1,188 | 1,473 | 24.3 |
| 200 | INSERT | 1,165 | 1,408 | 27.3 |
| 50 | SELECT | 1,064 | 1,301 | 24.4 |
| 100 | SELECT | 1,062 | 1,302 | 27.8 |
| 200 | SELECT | 1,057 | 1,125 | 28.3 |
オレゴンでは東京〜オレゴン間の RTT(約 100ms)× ハンドシェイクの往復回数分が加算され、med が 1,000ms を超える値になっています。
並列実行(再利用あり)
続いてコネクション再利用ありの結果です。
東京→東京
| 並列度 | op | med(ms) | p95(ms) | rps |
|---|---|---|---|---|
| 50 | INSERT | 10 | 12 | 48.4 |
| 100 | INSERT | 10 | 12 | 58.1 |
| 200 | INSERT | 9 | 11 | 63.3 |
| 50 | SELECT | 4 | 5 | 64.0 |
| 100 | SELECT | 4 | 5 | 63.9 |
| 200 | SELECT | 4 | 5 | 66.4 |
東京→オレゴン
| 並列度 | op | med(ms) | p95(ms) | rps |
|---|---|---|---|---|
| 50 | INSERT | 106 | 1,487 | 36.4 |
| 100 | INSERT | 106 | 107 | 62.3 |
| 200 | INSERT | 105 | 107 | 60.0 |
| 50 | SELECT | 100 | 102 | 62.3 |
| 100 | SELECT | 100 | 102 | 74.5 |
| 200 | SELECT | 100 | 101 | 69.6 |
コネクション再利用により、東京→東京の INSERT は med 9〜10ms、SELECT は 4ms に改善しました。オレゴンも med 100〜106ms まで低下し、純粋な RTT のみになっています。
5. 書き込みスループット
1 つの Lambda が 1,000 件を逐次 INSERT し、それを並列度を変えて実行しました。コネクション再利用あり、東京→東京の構成です。各並列度はシーケンシャルに実行し、同時実行による干渉を排除しています。
| 並列度 | 合計件数 | wall(s) | 合計 rps | Lambda 単体 rps |
|---|---|---|---|---|
| 1 | 1,000 | 9.7 | 103 | 105 |
| 10 | 10,000 | 10.0 | 1,002 | 111 |
| 20 | 20,000 | 9.8 | 2,048 | 113 |
| 30 | 30,000 | 9.8 | 3,065 | 117 |
| 40 | 40,000 | 10.2 | 3,905 | 116 |
| 50 | 50,000 | 10.0 | 4,996 | 116 |
| 100 | 100,000 | 20.0 | 5,000 | 117 |
Lambda 単体の INSERT スループットは約 115 rps で安定しており、並列化でほぼ線形にスケールします。50 並列で約 5,000 rps に達しました。c=100 で合計 rps が伸びないのは Step Functions Map ステートの内部バッチ分割によるもので、DSQL 側の上限には到達していません。
6. シングル vs マルチリージョン
シングルリージョンとマルチリージョン(東京+ソウル、witness: 大阪)で INSERT / SELECT の性能差を確認しました。クラスタを 2 セット作成し、それぞれ計測して再現性を確認しています。Lambda は東京から東京エンドポイントに接続しています。
クラスタ作成所要時間
| 構成 | 所要時間 |
|---|---|
| シングルリージョン | 約 8 秒 |
| マルチリージョン | 約 100〜115 秒 |
マルチリージョンは 3 ステップ(各リージョンにクラスタ作成 + 相互ピアリング)が必要で、ACTIVE になるまで約 2 分かかります。
INSERT(再利用あり, 東京→東京)
| 並列度 | シングル med(ms) | マルチ med(ms) | 倍率 |
|---|---|---|---|
| 50 | 10 | 65 | 6.5x |
| 100 | 10 | 64 | 6.4x |
| 200 | 9 | 64 | 7.1x |
SELECT(再利用あり, 東京→東京)
| 並列度 | シングル med(ms) | マルチ med(ms) | 倍率 |
|---|---|---|---|
| 50 | 4 | 4 | 1.0x |
| 100 | 4 | 4 | 1.0x |
| 200 | 4 | 4 | 1.0x |
INSERT はマルチリージョンで約 6〜7 倍遅くなりました。マルチリージョンでは強い一貫性を保証するためコミット時にリージョン間の同期オーバーヘッドが発生するためです。一方 SELECT はローカルリージョンで完結するため差がありません。
7. まとめ
単一テーブルへの単純な INSERT / SELECT による小規模な検証ですが、DSQL の性能特性と、サーバーレス環境から利用する際の勘所を明確にできました。
最大のポイントは DB コネクションの再利用です。再利用により INSERT は 10ms 以下、SELECT は 4〜6ms となり、都度接続と比べて 10〜20 倍改善します。書き込みスループットも、Lambda 単体(約 115 rps)から 50 並列で 5,000 rps までほぼ線形にスケールすることを確認できました。
また、マルチリージョン構成では強い一貫性を保証する同期オーバーヘッドにより、INSERT が約 6〜7 倍遅くなります。一方、ローカル読み取りとなる SELECT 性能は変わりません。可用性と書き込み性能のトレードオフには注意が必要です。
重要な注意点は、DSQL の接続レート制限(100 接続/秒・緩和不可のハードリミット)です。Lambda から実戦投入する際はコネクションの再利用を大前提とし、スパイク時のコールドスタート集中を避けるため、Provisioned Concurrency の活用や同時実行数の制御が不可欠です。
マルチリージョンやクロスリージョンで高いスループットが要求されるケースでは、DSQL への直接アクセスを減らす工夫が重要になります。書き込みは SQS などでバッファリングする非同期バルク処理が有効です。一方、読み取りはリードレプリカに頼れないため、アプリケーション側のキャッシュや CloudFront によるページキャッシュの活用など、クラウドネイティブな全体設計をご検討ください。






