サーバーレスアーキテクチャにおけるレイテンシの課題
サーバーレスコンピューティングは、無限のスケーラビリティとメンテナンスフリーという夢を私たちに見せてくれました。しかし、長年、データベースがボトルネックとして立ちはだかってきました。初めてCloudflare Workerをグローバルにデプロイした時のことを覚えています。コードの実行は10ms以下と非常に高速でしたが、シンガポールのユーザーにとっては実際のクエリを含むリクエスト時間が320msもかかっていました。原因は明らかでした。データベースがバージニア北部(us-east-1)の単一のAWSリージョンに固定されていたからです。
MySQL、PostgreSQL、MongoDBのどれを好むにせよ、ロジックをエッジに移動させると、従来の集中型データベースは物理的な壁となります。ユーザーがアプリを操作するたびに、わずか数バイトのデータを取得するためだけに信号が数千マイルも移動しなければなりません。この往復の遅延(ラウンドトリップタイム)が、そもそもエッジコンピューティングを使用することで得られるパフォーマンスのメリットを打ち消してしまうのです。
根本原因:距離と接続のオーバーヘッド
サーバーレスアプリのパフォーマンスを低下させる主な要因は、地理的な距離と接続のオーバーヘッドの2つです。PostgreSQLのような標準的なリレーショナルデータベースは、サーバーレス関数の「一瞬で起動して終了する」という性質を想定して設計されていません。これらは安定した長時間の接続を前提としています。関数が起動すると、データベースとのハンドシェイクだけで50msから100msを費やすことがよくあります。これに物理的な距離が加わると、ユーザー体験は目に見えて低下します。
Cloudflare D1は、コードが存在する場所にデータを配置することで、この問題を解決します。SQLiteをベースに構築されたD1は、Cloudflareのネットワークに直接組み込まれたネイティブなサーバーレスSQLデータベースです。データとやり取りするために、複雑なコネクションプーリングやVPCトンネルの設定を心配する必要はありません。
アーキテクチャの比較
なぜD1が根本的な転換点となるのか、数値と構造を見てみましょう。
1. 集中型モデル(従来の手法)
- データベース:
us-east-1で管理されるRDS。 - コンピュート: 分散されたエッジ関数。
- 注意点: グローバルユーザーに対する高いレイテンシ(200ms以上)。また、トラフィックの急増時にデータベースがクラッシュするのを防ぐために、PgBouncerのようなプロキシが必要です。
2. エッジネイティブモデル(Cloudflare D1)
- データベース: CloudflareのグローバルなPoP(Points of Presence)上に存在するD1 (SQLite)。
- コンピュート: Cloudflare Workers。
- 結果: 接続オーバーヘッドがほぼゼロ。データはユーザーの近くでレプリケートまたはキャッシュされ、場所に関係なくTTFB(Time to First Byte)が50ms未満に短縮されることがよくあります。
D1モデルのメリットとデメリット
どんなツールも万能薬(シルバーバレット)ではありません。プロダクション環境のスタック全体をSQLite-at-the-edgeに移行する前に、トレードオフを理解しておく必要があります。
長所
- ゼロ構成: パスワードやIP許可リストの管理は不要です。設定ファイル内のシンプルなバインディングを介してデータベースにアクセスできます。
- 予測可能な価格設定: D1には寛大な無料枠(1日あたり500万行の読み取り)があります。有料プランでは使用した分だけ支払うため、通常は24時間稼働のRDSインスタンスのコストの数分の一で済みます。
- 標準的なSQL: 独自のクエリ言語を学ぶ必要はありません。SQLiteで基本的な
SELECT文が書ければ、すでにD1の使い方は知っているも同然です。
制限事項
- サイズの制約: D1はバルクデータではなく速度を重視して設計されています。有料プランでは最近10GBまで上限が引き上げられましたが、テラバイト単位のログデータを保存するのには適していません。
- SQLiteの方言: SQLiteは軽量です。Postgresにあるような特定のウィンドウ関数やJSONBのようなカスタムデータ型など、一部の強力な機能が欠けています。
モダンなサーバーレススタック
今日、新しいプロジェクトを開始するのであれば、最高の開発体験を得るために以下の組み合わせをお勧めします。
- 言語: TypeScript(本番環境にデプロイする前にエラーをキャッチするため)。
- フレームワーク: Hono — エッジ向けに構築された、非常に小さく高速なWebフレームワーク。
- ORM: Drizzle ORM。軽量で、SQLクエリに対して完全な型安全性を提供します。
- CLI: すべてのデプロイにWranglerを使用。
実装:製品カタログの構築
D1データベースをセットアップし、5分以内にWorkerに接続する方法を紹介します。
ステップ1:プロジェクトの開始
ターミナルを開き、Cloudflareのイニシャライザを実行します。
npm create cloudflare@latest my-d1-app
# "Hello World" Workerを選択
# TypeScriptを選択
cd my-d1-app
ステップ2:データベースの初期化
Wrangler CLIを使用してデータベースインスタンスを作成します。これにより、Cloudflareのダッシュボードにデータベースが自動的に登録されます。
npx wrangler d1 create product-db
ターミナルに表示されたIDをコピーし、wrangler.tomlファイルに貼り付けます。
[[d1_databases]]
binding = "DB"
database_name = "product-db"
database_id = "あなたのユニークなデータベースID"
ステップ3:テーブルの作成
プロジェクトのルートにschema.sqlファイルを作成し、構造を定義します。
CREATE TABLE products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
price REAL NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO products (name, price) VALUES ('メカニカルキーボード', 99.99), ('USB-C ハブ', 49.50);
このスキーマをローカル環境と本番データベースの両方に反映させます。
# ローカルテスト用
npx wrangler d1 execute product-db --local --file=./schema.sql
# 本番データベース用
npx wrangler d1 execute product-db --remote --file=./schema.sql
ステップ4:Workerでのデータ取得
src/index.tsを更新して、リクエストを処理できるようにします。DBバインディングを使用して製品をクエリします。
export interface Env {
DB: D1Database;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const { pathname } = new URL(request.url);
if (pathname === "/products") {
const { results } = await env.DB.prepare(
"SELECT * FROM products"
).all();
return Response.json(results);
}
return new Response("見つかりません", { status: 404 });
},
};
ステップ5:本番公開
ローカルですべてをテストし、クエリが期待通りに動作することを確認します。
npx wrangler dev
準備ができたら、1つのコマンドでCloudflareのグローバルネットワークにデプロイします。
npx wrangler deploy
最後に
集中型のMySQLインスタンスからCloudflare D1に移行するのは、古いHDDからNVMe SSDにアップグレードするような感覚です。スピードの向上を肌で感じることができます。D1はまだ進化の途中ですが、エッジで実際に動作する使い慣れたSQLワークフローを提供してくれます。高トラフィックのAPIや、ミリ秒単位の差がユーザー維持率に影響するリアルタイムアプリを構築しているなら、D1は現在、インフラ管理の煩わしさなしにデータを管理できる最も効率的な方法の一つです。

