Cloudflare D1はどんなときに使うのか — 用途と設計パターンを徹底整理

CloudflareのサーバーレスSQLiteデータベースD1は「何に向いていて、何には向いていないのか」。公式ドキュメントの定義から実際の設計パターン、2025年に入ったグローバルリードレプリケーションまで、用途を軸に深掘りする。

·
  • Cloudflare D1
  • Cloudflare Workers
  • SQLite
  • サーバーレス
  • マルチテナント
  • アーキテクチャ
地球儀を取り囲む複数のデータベースアイコン、エッジデータベースのイメージイラスト

前回の投稿でDrizzle ORMとD1の接続方法を整理した。では D1 はそもそも何のために使うデータベースなのか。今回はそこを掘り下げる。

Cloudflare D1 はただの「Workersで使えるSQLite」ではない。その設計には明確な思想があり、得意な用途と苦手な用途がはっきり分かれている。それを理解せずに採用すると、後から別のストレージへの移行を強いられることになる。

D1 の基本的なポジション

Cloudflare は自社のドキュメントの中で D1 を次のような用途向けと位置づけている。

  • ユーザーデータ・アカウントデータなど構造化されたデータの永続化
  • SQL で横断的に問い合わせる必要があるユースケース
  • 読み取りが書き込みより多いワークロード(一般的なWebアプリの大半)

逆に言えば、書き込みが非常に多いケース、単一DBに大量データを集約したいケースは想定外だ。

D1 が得意な用途

1. Cloudflare エコシステム完結のフルスタックアプリ

Hono + D1、Astro + D1、Remix + D1。これらはすでに実績のある構成で、API サーバーからデータ永続化まで Cloudflare のプラットフォーム上で完結する。接続文字列もコネクションプールも不要で、Workers のバインディングとして D1 を直接渡せるのは設計上の大きな利点だ。

// Hono + D1 の例
import { Hono } from "hono";
import { drizzle } from "drizzle-orm/d1";

const app = new Hono<{ Bindings: { DB: D1Database } }>();

app.get("/posts", async (c) => {
  const db = drizzle(c.env.DB);
  const posts = await db.select().from(postsTable).all();
  return c.json(posts);
});

Lambda や Cloud Run のような他のサーバーレス環境では、データベース接続をコールドスタート時に確立しなければならず、その遅延がUXに直撃する。D1 はバインディングとしてランタイムに組み込まれているため、この問題が原理的に発生しない。

2. マルチテナント SaaS ── 「テナントごとにDBを1つ」

これが D1 の設計思想の核心だ。

Cloudflare の公式ドキュメントには明記されている。D1 は1つの大きなデータベースではなく、複数の小さなデータベースを水平にスケールアウトする設計を想定している。1 アカウントで最大 50,000 データベースを持てることが、その証拠だ。

SaaS を構築するとき、テナント間のデータ分離には大きく 3 つのアプローチがある。

アプローチ実装分離レベル
行レベル分離1つのDBにtenant_idカラム論理分離(低コスト)
スキーマ分離1つのDBに複数スキーマ論理分離(中コスト)
DB分離テナントごとに独立したDB物理分離(高コスト)

D1 が特に力を発揮するのはDB分離のアプローチだ。

// テナント作成時にD1データベースを動的に払い出す
async function provisionTenant(tenantId: string, env: Env) {
  // Workers API を使って D1 DB を動的に作成
  const response = await fetch(
    `https://api.cloudflare.com/client/v4/accounts/${env.CF_ACCOUNT_ID}/d1/database`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${env.CF_API_TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ name: `tenant-${tenantId}` }),
    }
  );
  const { result } = await response.json();
  return result.uuid; // 新しいDBのID
}

各テナントが独立したSQLiteファイルを持つため、あるテナントのスキーマ変更が他に影響しない。マイグレーション失敗の影響範囲を1テナントに限定できる。コンプライアンス上の要件で物理的なデータ分離が求められる業種(医療、金融、法律)にも対応しやすい。

Cloudflare 自身もこのパターンを推奨している。ドキュメントには「ユーザーごと・テナントごと・エンティティごとのデータベース」という用語が繰り返し登場する。

3. グローバルユーザーを持つ読み取り重視のアプリ

2025年4月、D1 はグローバルリードレプリケーション機能をパブリックベータとして公開した。この機能は 追加コストなしで利用できる。

仕組みはこうだ。D1 には「プライマリ」と呼ばれる書き込みの実体が1つある。そこに加えて、Cloudflare のネットワーク上の複数リージョンに読み取り専用の「リードレプリカ」が自動作成される。ユーザーのリクエストは近いレプリカにルーティングされるため、プライマリが遠くにあっても読み取りのレイテンシが短縮される。

ただし、非同期レプリケーションの宿命として「レプリカが最新状態より遅れる可能性」がある。D1 はこれを Sessions API と「ブックマーク」という仕組みで解決している。

export default {
  async fetch(request: Request, env: Env) {
    // 前のリクエストからブックマークを引き継ぐ
    const bookmark = request.headers.get("x-d1-bookmark") ?? "first-unconstrained";

    // セッションを開始。このセッション内は逐次整合性が保証される
    const session = env.DB.withSession(bookmark);

    const result = await session.prepare(
      "SELECT * FROM posts ORDER BY created_at DESC LIMIT 20"
    ).all();

    return Response.json({
      data: result.results,
      bookmark: session.getBookmark(), // 次のリクエストに渡す
    });
  },
};

「自分の書き込みを自分で読める(read your own writes)」という直感的な一貫性保証が、Sessions API を使うことで維持される。ECサイト、CMS、モバイルアプリのバックエンドなど、グローバルに分散したユーザーに低レイテンシで読み取りを提供したいケースに向く。

4. エッジで完結する小〜中規模 API

Cloudflare Workers はデフォルトで世界 300 以上の都市で動作する。D1 をバックエンドにすることで、「アプリも DB も近くにいる」状態を作れる。

Cloudflare はコスト面でも興味深い指摘をしている。Workers は CPU 時間のみを課金する仕組みのため、データベースクエリや外部 API 呼び出しで待機している時間は料金が発生しない。データベース接続で待機している時間ぶんも課金される Lambda のような構造とは根本的に異なる。

5. AI エージェント・AI ゲートウェイのバックエンド

Cloudflare 自身が AI Gateway の内部ストレージとして D1 を使っていることをブログで公開している。数十億リクエストのログを Workers + D1 + Durable Objects + R2 の組み合わせで処理しているという実例だ。

AI エージェントが扱うような「ユーザーごとの会話履歴」「セッション状態」「設定データ」は D1 と相性がよい。書き込みは比較的少なく、読み取りが中心になるためだ。Cloudflare の Durable Objects が会話の状態を保持し、D1 が永続的な記録を担うというパターンが実際によく使われる。

D1 が苦手な用途

高スループットの書き込み

D1 はDurable Objects上に構築されており、書き込みは複数ロケーションにわたる耐久性のある永続化が必要なため、本質的に書き込みが重い。大量イベントのリアルタイムロギング、ソーシャルフィードのファンアウト書き込み、IoTデバイスからの大量センサーデータ収集などは向いていない。

こうした用途には Cloudflare Queues と組み合わせるのが定石だ。即座の書き込みはキューに積んで非同期でバッチ処理し、D1 への書き込みを間引く。

ユーザーリクエスト → Worker → Cloudflare Queues → Consumer Worker → D1 (バッチ書き込み)

1DBに10GBを超えるデータ集約

D1 の1データベースあたりの上限は10GBで、これは引き上げ不可の制約だ。ここに大量データを収めようとするのは設計の誤りになる。前述のとおり、D1 は「小さいDBを大量に作る」モデルのためのサービスだ。

数百GBや数TBのデータを単一のリレーショナルDBに集約したい場合は、Neon(PostgreSQL)や PlanetScale(MySQL)、あるいは Cloudflare の Hyperdrive(既存DBのエッジアクセラレータ)の方が適している。

ただしアカウント全体では、Workers有料プランで 1TBまでのD1ストレージが利用可能になった(2025年の更新)。これは10GBのDBを100個以上作れることを意味しており、大規模マルチテナント構成では十分な余地がある。

複雑なトランザクションが多いワークロード

D1 は SQLite の分離レベルを継承しており、スナップショット分離を提供する。ただし、複数のデータベースにまたがるトランザクション、強いACID保証が必要な金融決済処理、大量の行をロックするような複雑な書き込みトランザクションは想定外だ。

ストレージ選択の判断基準

Cloudflare エコシステムには D1 以外にも複数のストレージ選択肢があり、用途によって使い分ける。

ストレージ向いている用途
D1ユーザーデータ、アカウント情報、SQLで検索したい構造化データ、読み取り重視
Workers KVセッションデータ、APIキー、設定値。高頻度読み取り・低頻度書き込み
Durable Objectsリアルタイム状態管理、WebSocket、分散システムの調整
R2画像・動画・ファイルなど大容量の非構造化データ
Hyperdrive既存のPostgresやMySQLをエッジから高速アクセス

「ユーザーのプロフィールや投稿を保存したい」→ D1。「ログインセッションを管理したい」→ KV。「リアルタイムチャットの部屋の状態を管理したい」→ Durable Objects。このような切り分けが基本になる。

まとめ:D1 が「正解」になるアーキテクチャ

整理すると、D1 が真価を発揮するのは次の3つの条件がそろったときだ。

① Cloudflare Workers 上で動くアプリのバックエンドである
バインディングによる直接アクセスが最大の強みであり、これを使わない理由はない。

② 読み取りが書き込みより圧倒的に多い
ECサイトの商品一覧、ブログの記事表示、ユーザープロフィール取得。読み取り重視のWebアプリがそのまま当てはまる。グローバルリードレプリケーションが加わって、この強みはさらに拡大した。

③ データをテナント・ユーザー単位で分割できる(あるいは分割したい)
1つの巨大DBを育てるのではなく、「1テナント1DB」で小さな単位を大量に管理する思想を受け入れられる設計なら、D1 はむしろ理想に近い。

Cloudflare のプラットフォーム上で完結するスタックとして見たとき、D1 は Workers・R2・KV と同じく「インフラのことを考えなくてよい」体験を SQL の世界に持ち込んだプロダクトだ。接続文字列を管理しなくてよい、コネクションプールを考えなくてよい、リージョンを選ばなくてよい。その代わりに「10GBの上限」と「書き込みスループットの限界」という制約がある。

制約を理解した上で使えば、D1 は想像以上に快適な選択肢になる。


関連リンク

Last updated