記事の一覧

Cloudflare D1 とは?料金・制限・使い方と向いている用途

Cloudflare D1 は SQLite ベースのサーバーレス DB です。Workers からの使い方、DB サイズや無料枠などの制限と料金、Time Travel、リードレプリカ、向いている用途を 2026 年 11 月時点の公式情報で整理します。

Cloudflare D1 は Cloudflare Workers から使える SQLite ベースのデータベースです。サーバーを立てずに SQL のデータベースが使え、無料枠もあるので、個人開発から業務システムまで使う人が増えています。一方で、1 つの DB の大きさや処理の速さには、普通の RDB とは違う制限があります。この記事では D1 の仕組み、Workers からの使い方、制限と料金、Time Travel とリードレプリカ、どんな用途に向くかをまとめます。

制限と料金の数字は、2026 年 11 月時点の Cloudflare の公式ドキュメントのものです。変わることがあるので、使う前に公式のページも確かめてください。

Cloudflare D1 とは

D1 は Cloudflare が運用するサーバーレスのデータベースです。公式ドキュメントでは「SQLite の SQL の意味論を持つ、マネージドなサーバーレスデータベース」と説明されています。サーバーレスとは、DB サーバーの台数やスペックを自分で決めたり管理したりせず、使った分だけ課金される形のことです。

中身は SQLite のクエリエンジンなので、書ける SQL はおおむね SQLite と同じです。JSON 関数、全文検索の FTS5、数学関数などの拡張も一部使えます。PostgreSQL や MySQL のように、ネットワーク越しに常時接続して使う DB とは作りが違い、主に Workers のコードから「バインディング」という仕組みで呼び出します。

D1 には次のような特徴があります。

  • 1 つの DB は最大 10 GB (有料プラン) と小さめで、そのかわりに DB をたくさん作れる。有料プランでは 10 万個まで追加料金なしで作れ、ユーザーごと、テナントごとに DB を分ける設計を想定している
  • バックアップと復元の仕組み (Time Travel) が最初から有効
  • 読み取り専用のコピー (リードレプリカ) を世界の各地域に置ける
  • データの転送量 (egress) には課金されない

Workers から D1 を使う

DB を作ってバインディングを設定する

Cloudflare の CLI である Wrangler で DB を作ります。

npx wrangler@latest d1 create my-app-db

作成すると、DB の ID が表示されます。Workers の設定ファイル (wrangler.jsonc) に、この DB を使うためのバインディングを書きます。作成時に「Wrangler に追加してもらうか」を聞かれたときに Yes を選ぶと、自動で追記されます。

{
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "my-app-db",
      "database_id": "<作成時に表示された ID>"
    }
  ]
}

binding に書いた名前 (ここでは DB) で、Worker のコードから env.DB として使えます。

テーブルを作る

SQL のファイルを用意して、wrangler d1 execute で流します。--local は手元の開発用 DB、--remote は Cloudflare 上の本番の DB です。

-- schema.sql
CREATE TABLE IF NOT EXISTS orders (
  id INTEGER PRIMARY KEY,
  customer_id INTEGER NOT NULL,
  amount INTEGER NOT NULL,
  status TEXT NOT NULL,
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_orders_created_at ON orders(created_at);
npx wrangler d1 execute my-app-db --local --file=./schema.sql
npx wrangler d1 execute my-app-db --remote --file=./schema.sql

テーブルの変更を履歴として管理したいときは、wrangler d1 migrations create でマイグレーションのファイルを作り、wrangler d1 migrations apply で適用する方法もあります。

Worker からクエリを実行する

export interface Env {
  DB: D1Database;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const { results, meta } = await env.DB
      .prepare("SELECT id, amount, status FROM orders WHERE customer_id = ? ORDER BY id DESC LIMIT 20")
      .bind(123)
      .run();

    console.log("rows_read:", meta.rows_read);
    return Response.json(results);
  },
} satisfies ExportedHandler<Env>;

prepare() で SQL を用意し、bind() で ? に値を入れ、run() で実行します。値を SQL の文字列に直接埋め込まず bind() で渡すことで、SQL インジェクションを防げます。結果を取り出す方法はほかに、1 行目だけを返す first()、行を配列の配列で返す raw() があります。run() と all() は同じものです。

複数の SQL をまとめて実行するときは batch() を使います。

await env.DB.batch([
  env.DB.prepare("UPDATE orders SET status = 'paid' WHERE id = ?").bind(1001),
  env.DB.prepare("INSERT INTO payments (order_id, amount) VALUES (?, ?)").bind(1001, 4980),
]);

batch() の中の文は 1 つのトランザクションとして順に実行され、途中で失敗すると全体が取り消されます。DB への往復も 1 回で済むので、関係する書き込みはできるだけ batch() にまとめます。

D1 の制限

主な制限を表にまとめました (2026 年 11 月時点、公式ドキュメントより)。無料プランは Workers Free、有料プランは Workers Paid のことです。

項目 無料プラン 有料プラン
アカウントあたりの DB の数 10 1,000,000 (10 万個まで料金に含む)
1 つの DB の最大サイズ 500 MB 10 GB (引き上げ不可)
アカウント全体のストレージ 5 GB 1 TB (申請で引き上げ可)
Time Travel で戻せる期間 7 日 30 日
1 回の Worker の実行あたりのクエリ数 50 1,000
1 つの SQL の実行時間 30 秒 30 秒

どちらのプランでも、1 テーブルの列は 100 個まで、1 行や文字列・BLOB は 2 MB まで、SQL 文の長さは 100 KB まで、1 つのクエリに渡せるパラメータは 100 個までです。2026 年 11 月 4 日からは、1 回のクエリの結果は 50 MB までになります。

1 つの DB はクエリを 1 つずつ処理する

制限の中で、設計にもっとも影響するのがこの点です。公式ドキュメントには、D1 の 1 つの DB はシングルスレッドで動き、クエリを 1 つずつ処理すると書かれています。

そのため、1 秒あたりに処理できるクエリの数は、1 つのクエリにかかる時間で決まります。平均 1 ミリ秒のクエリならおよそ 1,000 件、平均 100 ミリ秒のクエリならおよそ 10 件です。処理待ちの列があふれると、「overloaded」のエラーが返ります。

この性質から、次のような書き方が大切になります。

  • WHERE や ORDER BY に使う列にインデックスを張り、1 つのクエリを短くする
  • 小さなクエリを何回も投げるより、batch() でまとめる
  • 大量の更新や削除は、1,000 行ずつのように分けて実行する
  • 書き込みが多いサービスは、ユーザーやテナントごとに DB を分けて負荷を散らす

D1 の料金と無料枠

D1 の料金は読んだ行数、書いた行数、保存しているデータの量で決まります (2026 年 11 月時点、公式ドキュメントより)。

項目 無料プラン 有料プラン
読み取った行 1 日 500 万行 月 250 億行まで含む、超過は 100 万行あたり $0.001
書き込んだ行 1 日 10 万行 月 5,000 万行まで含む、超過は 100 万行あたり $1.00
ストレージ 合計 5 GB 5 GB まで含む、超過は 1 GB・月あたり $0.75
DB の数 10 10 万個まで含む、超過は 1 個・月あたり $0.10

有料プランは Workers Paid で、アカウントあたり月 $5 の最低料金がかかります。データ転送量の課金はありません。無料プランの 1 日の上限は UTC の 0 時 (日本時間の 9 時) にリセットされ、上限に達するとその日はクエリが実行できなくなります。

「読み取った行」はスキャンした行数

料金を考えるうえで知っておきたいのは、「読み取った行」が、結果として返した行数ではなく、クエリがスキャンした行数だということです。5,000 行のテーブルを全件スキャンすれば、結果が 1 行でも 5,000 行と数えます。

インデックスがあれば、スキャンする行が減り、読み取った行も減ります。ただし、インデックスのある列に書き込むと、書き込んだ行が少なくとも 1 行増えます。公式ドキュメントでは、ほとんどの場合、読み取りが減る効果のほうが大きいとされています。

実際に何行読んだかは、クエリの結果の meta.rows_read と meta.rows_written で確かめられます。上の Worker のコードのように、開発中にログへ出しておくと、インデックスが効いているかを数字で確かめられます。Wrangler やダッシュボードから実行したクエリも、読み取りや書き込みとして数えられる点に気をつけてください。

Time Travel でバックアップから戻す

Time Travel は D1 に組み込まれたバックアップと復元の仕組みです。自分で有効にしたり、バックアップの時刻を決めたりする必要はありません。有料プランでは過去 30 日、無料プランでは過去 7 日の範囲で、任意の 1 分単位の時点に DB を戻せます。履歴の保持と復元に追加の料金はかかりません。

操作は Wrangler で行います。

# 指定した時刻のブックマーク (復元点) を確認する
npx wrangler d1 time-travel info my-app-db --timestamp=2026-11-01T09:00:00+09:00

# その時刻に戻す (2026-11-01 09:00 日本時間を Unix 時刻で指定した例)
npx wrangler d1 time-travel restore my-app-db --timestamp=1793491200

復元は今の DB を上書きする操作です。実行中のクエリやトランザクションは取り消され、クライアントにはエラーが返ります。そのかわり、復元を実行すると直前の状態を指すブックマークが返るので、それを --bookmark で指定すれば復元を取り消せます。

30 日より長く残したい場合は、公式ドキュメントでは R2 (Cloudflare のオブジェクトストレージ) にエクスポートして保管する方法が紹介されています。また、2026 年 11 月時点では、DB を複製して別の DB を作る機能はまだありません。

リードレプリカで読み取りを速くする

D1 の DB には、プライマリ (書き込みを受けるもとの DB) が 1 つあります。リードレプリカを有効にすると、北米東部・北米西部・西ヨーロッパ・東ヨーロッパ・アジア太平洋・オセアニアの各地域に読み取り専用のコピーが置かれ、利用者に近いところで読み取りを処理できます。レプリカごとの追加料金はなく、読み取った行と書き込んだ行の数で課金される点は同じです。

有効にするのは、ダッシュボードの DB の Settings から「Enable Read Replication」を選ぶか、REST API で設定します。

レプリカには少し遅れてデータが届くので、書いた直後に別のレプリカから読むと、書いたはずのデータが見えないことがあります。これを避けるために、Sessions API を使います。

const bookmark = request.headers.get("x-d1-bookmark") ?? "first-unconstrained";
const session = env.DB.withSession(bookmark);

const { results } = await session
  .prepare("SELECT id, status FROM orders WHERE customer_id = ?")
  .bind(123)
  .run();

response.headers.set("x-d1-bookmark", session.getBookmark() ?? "");

セッションの中のクエリは、順序の一貫性が保たれます。自分が書いたものはそのあとの読み取りで見え、あとの読み取りが前の読み取りより古いデータを返すこともありません。ブックマークをヘッダーなどで受け渡せば、次のリクエストでも同じ一貫性を保てます。最初のクエリから最新のデータが必要なら withSession("first-primary") でプライマリから始めます。

Sessions API は Worker のバインディングからだけ使えます。どこでクエリが処理されたかは、meta.served_by_region と meta.served_by_primary で確かめられます。

D1 が向いている用途、向いていない用途

ここまでの特徴をもとに、向き不向きを整理します。

D1 が向いているのは、次のような用途です。

  • Workers や Pages で作る Web アプリ、API の DB。設定が少なく、同じ Cloudflare の中で完結する
  • ユーザーごと、テナントごとに DB を分ける SaaS。DB を増やしても 1 個あたりのコストが小さい
  • 読み取りが中心で、世界の各地から使われるサービス。リードレプリカで読み取りを近くで処理できる
  • 個人開発や試作。無料プランで始められる

一方で、次のような場合は、ほかの DB も比べて選んだほうがよいでしょう。

  • 1 つの DB に 10 GB を超えるデータを入れる必要がある
  • 1 つの DB に書き込みが集中し、クエリを 1 つずつ処理する構造が足かせになる
  • Cloudflare の外のサーバーやツールから、PostgreSQL や MySQL のプロトコルで直接つなぎたい
  • 大量のデータを集計する分析が中心 (この用途なら BigQuery のような分析用の DB が合います。料金の考え方は BigQuery の料金の仕組みと抑え方 を見てください)

MySQL 互換で大きなデータを扱える DB を探しているなら、TiDB とは も参考になります。

D1 のデータを見張るには

D1 を本番で使い始めると、DB 自体の状態に加えて、中のデータが正常かどうかも気になってきます。たとえば、注文のテーブルに新しい行が入り続けているか、処理待ちのままのレコードが溜まっていないか、といったことです。ダッシュボードでは読み取った行数や書き込んだ行数、ストレージの推移を見られますが、業務の数字は自分で SQL を書いて確かめることになります。

手作業で続けるなら、確認用の SQL を Workers の Cron Triggers で定期的に実行し、結果が想定から外れたらチャットやメールに通知する仕組みを作るのが手軽です。何を見張るべきかの考え方は データベース監視とは にまとめています。

この確認を AI に任せる方法もあります。Scandock は Cloudflare D1 に対応したデータベースの監視サービスで、AI がテーブルの構成と実データを読んで、注文数やジョブの滞留など何を監視するかを決めます。固定の閾値ではなく値の推移を踏まえて異常かどうかを判断し、異常のときは読み取り専用のクエリで原因を調べて、根拠と対処案をつけて通知します。DB に詳しくない人でも、チャットで「先週から注文が減っていないか」と聞くと、AI がクエリで調べてグラフつきで答えます。

参考

関連する記事