結論から言うと、Cloudflare D1の料金を評価するとき、データベースの数を数えるのは無駄です。課金されるのはDBの個数ではなく、アカウント全体で合算されたスキャン行数だから。この一点を取り違えると、設計を不必要に窮屈にするか、あるいは請求書で驚くことになります。
「D1って安いんですかね」という値踏みの一言から調べ始めて、最終的に設計の考え方がひとつ変わったので、その過程を記録しておきます。
料金表を見ても「安い」しか分からない
D1はCloudflare Workersから使えるSQLiteベースのマネージドデータベースです。料金の入口はシンプルで、Workers Paid(月$5)に次の枠が含まれています。
| 課金軸 | Workers Paidに含まれる枠 | 超過分 |
|---|---|---|
| 読み取り行数(rows read) | 月250億行 | 100万行あたり$0.001 |
| 書き込み行数(rows written) | 月5,000万行 | 100万行あたり$1.00 |
| ストレージ | 5GB | $0.75/GB・月 |
無料プランでも1日500万行の読み取り・10万行の書き込み・5GBストレージまで使えるので、個人プロジェクトなら実質タダで回ります。さらにエグレス(転送量)課金はゼロ、リードレプリカの増設にも追加料金なし、クエリを投げていない間はコンピュート課金も発生しません(scale-to-zero)。
Cloudflare D1 Pricing(公式ドキュメント)D1 pricing based on rows read, rows written, and storage with scale-to-zero billing.developers.cloudflare.com正直、250億行という数字は大きすぎて実感が湧きません。「安い」で終わりそうになります。ただ、単価をよく見ると気になる点がひとつ。書き込みは100万行あたり$1.00、読み取りは$0.001。書き込みは読み取りの1000倍高いんです。ログを1行ずつINSERTするような設計は、D1では避けたい。この時点で「読み書きを同じ感覚で扱ってはいけないDB」だと分かります。
rows readの定義が罠——「返した行数」ではない
もうひとつ、課金の定義を読んで引っかかったのがrows readの意味です。課金対象は返した行数ではなく、スキャンした行数。5,000行のテーブルに SELECT * FROM table を打てば5,000行の読み取り扱いになりますし、インデックスのない列でフィルタすると、返る行が1行でも、スキャンした分がまるごとカウントされます。
これがどれくらい効くかというと、76.5万行程度のテーブルを持つ公開サイトで、月に1,276億行(127,599,130,859行)の読み取りが発生し、請求が$134.14になったという報告があります。内訳の$127.60、実に95%が読み取り課金。報告者は複合インデックスとANALYZE、KVによるキャッシュ層の追加で読み取りを95%削減したそうです。
How I Reduced My Cloudflare D1 Bill(外部の実例報告)Cloudflare D1 pricing punishes unindexed queries. I racked up a $134 bill before fixing it with composite indexes, ANALYZE, and a KV cache layer for SvelteKit.fullstacksveltekit.comこれは他の方の事例で、私自身が踏んだわけではありません。ただ「安い」の裏に、設計次第で一桁二桁変わる構造があることはよく分かります。テーブルが76.5万行でも、インデックスが効いていなければ読み取りは億の単位に膨らむ。料金表の数字より、この定義のほうがずっと重要でした。
「4つDBがあったら$20?」——DB数は課金に関係なかった
ここまで調べて、次に浮かんだのは素朴な疑問でした。「$5のプランは、すべてのDB含めて$5? それとも使用量に関わらず、4つDBがあったら$20とられる?」
答えは前者です。$5はアカウント単位で、DBが4つでも100個でも$5のまま。上の表の枠(250億行・5,000万行・5GB)も、アカウント全体の合算に適用されます。ストレージについては公式ドキュメントに「アカウント内の全データベースの合計」と明記されています。
それどころかCloudflareは、「分離のためにDBを数千個作っても追加料金はかからない」という趣旨のことを公式に書いています。マルチテナントでテナントごとにDBを分ける設計を、むしろCloudflare側が推奨している。
Cloudflare D1 Overview(公式ドキュメント)Build serverless SQL databases on Cloudflare's global network and query them from Workers and Pages projects.developers.cloudflare.comここで設計の考え方がひとつ覆りました。他のマネージドDBなら、コスト試算で真っ先に効いてくるのは「インスタンス数×単価」です。テナントごとにDBを分けたければ、その分だけ固定費が積み上がる。だから「料金が怖いので1つのDBに全テナントを押し込む」という判断が生まれます。D1ではこの分割コストがゼロ。料金体系がテナント分割にペナルティを課さない、という設計思想の違いでした。
DBは気軽に切っていい。監視するのは合算のrows readだけ
というわけで、D1ではDBを増やすかどうかは純粋に設計の判断でよくなります。テナントごと、用途ごとに気軽に切れる。分離しておけばテナント間のデータ混入をクエリのWHERE句だけで守る必要もなくなります。
ただし但し書きをひとつ。枠はアカウント合算なので、テナントが増えれば合計の読み取り行数は当然増えます。DBを分けたからといって枠が増えるわけではありません。監視すべき指標が「合算のrows read」ただひとつに集約される、というのが正確なところです。
その監視と対策は3点セットで考えています。
- WHERE / ORDER BYに使う列には必ずインデックスを張る。 書き込み時にインデックス更新分の行が1行増えますが、読み取り削減でほぼ確実にペイします(読み書きの単価差1000倍を思い出してください)
- クエリ結果の
metaオブジェクトを見る。 D1のレスポンスにはrows_read/rows_writtenが含まれるので、想定外のフルスキャンはここで検知できます - 読み取りが重い公開エンドポイントは前段でキャッシュする。 KVかCache APIを挟めば、D1に到達する読み取り自体を減らせます
インデックスさえ張っておけば、個人〜小規模サービスで250億行に届くのは相当難しい、というのが私の見立てです(公式が保証しているわけではないので、meta での実測は続けます)。
SQLiteを本番に使う判断そのものは別記事で書いています。D1はそのSQLiteのマネージド版なので、前提を確認したい方はこちらから。
SQLiteを本番DBに使う判断基準|「書き込み直列化」は弱点ではないSQLiteの「書き込みが同時1本に直列化される」制約が本番でどう効くかを解説します。ロックの正体と待ち時間の実感値、ECを例にした思考実験、唯一の事故パターンである長いトランザクション、在庫競合がむしろ単純になる理由、採用判断の目安までを整理します。www.tentspace.netCloudflareの料金は「使った分だけ、ただし課金軸の定義に癖がある」というパターンが多く、Durable Objectsのときも請求の本体は別の場所にありました。コスト設計の考え方はこちらの記事も同じ軸で書いています。
Durable Objectsの高額請求は怖くない|AIチャットのコストの本体はトークン代Cloudflare WorkersでSaaSにAIチャットを組み込むときの費用の見方を整理します。Durable Objectsの課金を実際に計算すると月16万チャットまで無料枠に収まり、請求の本体はモデルのトークン代でした。家計簿サービスの設計検討で出た試算とプラン逆算をまとめます。www.tentspace.net料金表の数字を眺めて「安い」と言うだけなら誰でもできます。効いてくるのは課金軸の定義まで読むこと。D1なら「DB数は数えなくていい、スキャン行数だけ数えろ」——これが今回の結論です。






