Web開発 2026年5月10日

Cloudflare D1

D1 は Cloudflare のサーバーレス SQL データベースであり、SQLite 互換のクエリと Workers バインディングで動作する。1 アカウントあたり最大 5 万 DB を作成でき、テナントごと・ユーザーごとに DB を分ける「水平スケールアウト型」設計が前提となる。

D1

一行サマリ

D1 は Cloudflare のサーバーレス SQL データベースであり、SQLite 互換のクエリと Workers バインディングで動作する。1 アカウントあたり最大 5 万 DB を作成でき、テナントごと・ユーザーごとに DB を分ける「水平スケールアウト型」設計が前提となる。

解決する課題(Why)

従来のマネージド RDB は「常時起動するインスタンス」を前提とし、Workers のようなエッジ実行環境からは接続プールやレイテンシで相性が悪い。D1 は以下を解決する。

  • 接続プール不要。Workers バインディング経由で直接クエリを発行できる。
  • スケール to ゼロ。クエリを実行しない時間は課金されない。
  • DB 作成コストが実質ゼロ。マルチテナント SaaS で「テナント=1 DB」設計が経済合理的に成立する。
  • バックアップ/PITR が標準装備(Time Travel)で運用負荷を持たない。

主要機能(What)

  • SQLite ベース:SQLite の SQL セマンティクスと型システムをそのまま採用。スキーマ・クエリは標準 SQLite と互換。
  • Workers Binding API / HTTP REST API / Wrangler CLI の 3 経路でアクセス可能。
  • Time Travel:過去 30 日(Free は 7 日)の任意の 1 分単位に DB を復元できるポイントインタイムリカバリ。バックアップ設定は不要で常時有効。
  • Global Read Replication:読み込みリードレプリカをグローバルに自動配置し、リード遅延を低減。書き込みは引き続きプライマリ(単一の Durable Object)に集約。
  • Batch / Prepared Statementdb.batch() でトランザクション的に複数文を実行。バインドパラメータは最大 100。
  • インポート/エクスポートwrangler d1 execute --file で SQL ダンプを取り込み可能(最大 5 GB)。

アーキテクト視点:いつ選ぶか

適しているシーン

  • Workers / Pages を前提としたエッジアプリケーションの永続層。
  • マルチテナント SaaS で「テナントごとに DB を分離したい」要件。1 アカウント 5 万 DB が利用可能。
  • 1 DB あたり 10 GB 以下に収まる、明確に分割できるドメインデータ。
  • 平均レスポンス 1 ms 〜 数十 ms の OLTP ワークロードで、書き込みが秒間数十〜数百以下に収まる規模。
  • バックアップ運用を持ちたくない小〜中規模プロジェクト。

適していないシーン(重要)

  • 書き込み TPS が高いワークロード:各 D1 DB は単一の Durable Object に紐づき、シングルスレッドで逐次処理される。平均クエリ 1 ms なら理論上限は約 1000 QPS、100 ms なら 10 QPS。書き込みは数 ms 単位で複数拠点に永続化されるため、単一 DB で秒間数百以上の書き込みは破綻する。
  • 1 DB で 10 GB を超えるデータ:DB あたり 10 GB は引き上げ不可。シャーディング設計が必須。
  • 長時間クエリ・大量更新:単一 SQL の実行上限は 30 秒。数十万行を一括 UPDATE / DELETE する DDL バッチは平台限界に抵触するため、1000 行単位等に分割する必要がある。
  • PostgreSQL 固有機能(拡張、JSONB、PostGIS、CTE 上の高度な最適化、ストアド)に依存するワークロード。
  • 強整合の地理分散書き込み:書き込みは単一プライマリ Durable Object に集約されるため、書き込み元のリージョンによってはレイテンシが大きくなる。

競合・代替

観点D1Neon (Postgres)Turso (libSQL)Supabase (Postgres)PlanetScale (MySQL)Hyperdrive + RDS/Postgres
エンジンSQLitePostgreSQLSQLite (libSQL)PostgreSQLMySQL/VitessPostgreSQL/MySQL
デプロイ形態サーバーレス(Workers 統合)サーバーレス(ブランチ機能)エッジ分散マネージドマネージド外部 DB を Workers 高速化
スケール to ゼロ対応対応対応一部非対応外部 DB に依存
書き込みスケール単一 DO(弱)単一プライマリ+スケール単一プライマリ強い非常に強い(シャーディング)バックエンド依存
マルチテナント DB 分離5 万 DB 無料プロジェクト/ブランチ単位数万 DB 対応1 プロジェクト 1 DBDB 数に課金不向き
Workers との相性ネイティブHyperdrive 経由ネイティブ/HTTPHyperdrive 経由Hyperdrive 経由ネイティブ
バックアップ/PITRTime Travel 30 日ブランチ+PITRブランチPITR(有料)ブランチRDS 機能
主用途エッジ OLTP・テナント分離型 SaaSフル機能 Postgres が必要なエッジエッジ分散 SQLitePostgres + Auth + Storage 統合大規模 OLTP既存 Postgres 資産の延命

選定基準:Workers 中心かつ DB を細かく分けられるなら D1、Postgres の機能や単一 DB の書き込みスループットが必要なら Neon+Hyperdrive、エッジ書き込み分散重視なら Turso、フルスタック BaaS なら Supabase。

料金モデルの要点

項目Workers FreeWorkers Paid
Rows read500 万 / 日月 250 億まで無料、超過分 $0.001 / 100 万行
Rows written10 万 / 日月 5000 万まで無料、超過分 $1.00 / 100 万行
Storage合計 5 GB5 GB 含む、超過分 $0.75 / GB-月
データ転送(egress)無料無料
読み込みレプリカ追加料金なし追加料金なし(通常の rows_read 課金のみ)
  • 課金はクエリ+ストレージのみ。インスタンス時間課金なし、転送量課金なし。
  • Rows read はスキャン行数。フルスキャンは行数ぶん丸ごと加算されるため、インデックス設計が直接コストに効く。
  • Rows written:INSERT / UPDATE / DELETE で書き込まれた行数。インデックスがある列を更新すると追加で書き込みが発生する。
  • 100 KB の行も 1 KB の行も 1 行は 1 行としてカウントされる。
  • 各クエリのレスポンスに含まれる meta.rows_read / meta.rows_written で実測可能。

CLI / IaC 操作例

Wrangler

# DB 作成
wrangler d1 create my-app-db

# wrangler.toml にバインディング登録
# [[d1_databases]]
# binding = "DB"
# database_name = "my-app-db"
# database_id = "<UUID>"

# マイグレーション
wrangler d1 migrations create my-app-db init
wrangler d1 migrations apply my-app-db --remote

# 任意 SQL の実行
wrangler d1 execute my-app-db --remote --command "SELECT count(*) FROM users"

# ダンプ取り込み
wrangler d1 execute my-app-db --remote --file ./seed.sql

# Time Travel
wrangler d1 time-travel info my-app-db
wrangler d1 time-travel restore my-app-db --bookmark <bookmark>

Drizzle ORM

// schema.ts
import { sqliteTable, integer, text } from "drizzle-orm/sqlite-core";

export const users = sqliteTable("users", {
  id: integer("id").primaryKey({ autoIncrement: true }),
  email: text("email").notNull().unique(),
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
});

// worker.ts
import { drizzle } from "drizzle-orm/d1";
import { eq } from "drizzle-orm";
import { users } from "./schema";

export interface Env { DB: D1Database }

export default {
  async fetch(req: Request, env: Env): Promise<Response> {
    const db = drizzle(env.DB);
    const url = new URL(req.url);
    const email = url.searchParams.get("email");
    if (!email) return new Response("email required", { status: 400 });

    const found = await db.select().from(users).where(eq(users.email, email)).all();
    return Response.json(found);
  },
};
# Drizzle Kit でスキーマからマイグレーション SQL を生成 → wrangler で適用
npx drizzle-kit generate
wrangler d1 migrations apply my-app-db --remote

制限・注意点

項目
1 アカウントの DB 数50,000(Paid)/ 10(Free)
1 DB の最大サイズ10 GB(引き上げ不可)
1 アカウントの総ストレージ1 TB(Paid)/ 5 GB(Free)
Time Travel 保持期間30 日(Paid)/ 7 日(Free)
1 Worker 呼び出しあたりのクエリ数1,000(Paid)/ 50(Free)
1 テーブルの最大カラム数100
1 行の最大サイズ2 MB
SQL ステートメントの最大長100 KB
1 クエリのバインドパラメータ最大100
単一クエリの最大実行時間30 秒
インポート最大ファイルサイズ5 GB
1 Worker から D1 への同時接続6
  • 書き込みは単一の Durable Object に集約される。リードレプリカを増やしても書き込みスループットは上がらない。
  • 書き込みプライマリのリージョンは選べない(自動配置)。書き込み元 Worker の地理位置によっては数十〜数百 ms のレイテンシが追加される。これがグローバル書き込みワークロードでは決定的な制約になる。
  • 大量バッチは分割必須。100 万行の UPDATE は 1000 行 × 1000 バッチに割って実行する設計を初期から入れる。
  • 過剰な並列リクエストはキューイングされ、キュー溢れで overloaded エラーになる。リトライ設計が前提。
  • ダッシュボードや Wrangler から実行したクエリも課金対象。

参考リンク


参照日: 2026-05-03