本文へスキップ
テクノロジー

Cloudflare D1の料金解説|課金されるのはDB数ではなくスキャン行数

Hirokuma
8分で読める
Cloudflare D1の料金解説|課金されるのはDB数ではなくスキャン行数
Hiro
D1データベースって安いんですかね
AI
安い部類です。ただ課金対象が「返した行数」じゃなく「スキャンした行数」なので、そこだけ注意で
Hiro
なるほど。ちなみに$5のプランって、すべてのDB含めて$5ですか?
Hiro
それとも使用量に関わらず、4つDBがあったら$20とられる?
AI
アカウント単位で$5です。DBの数は課金に一切関係ありません
Hiro
え、いくつ作っても変わらないんですか
AI
はい。Cloudflare自身が「分離のために数千個作っても追加料金なし」と書いています

結論から言うと、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点セットで考えています。

  1. WHERE / ORDER BYに使う列には必ずインデックスを張る。 書き込み時にインデックス更新分の行が1行増えますが、読み取り削減でほぼ確実にペイします(読み書きの単価差1000倍を思い出してください)
  2. クエリ結果の meta オブジェクトを見る。 D1のレスポンスには rows_read / rows_written が含まれるので、想定外のフルスキャンはここで検知できます
  3. 読み取りが重い公開エンドポイントは前段でキャッシュする。 KVかCache APIを挟めば、D1に到達する読み取り自体を減らせます

インデックスさえ張っておけば、個人〜小規模サービスで250億行に届くのは相当難しい、というのが私の見立てです(公式が保証しているわけではないので、meta での実測は続けます)。

SQLiteを本番に使う判断そのものは別記事で書いています。D1はそのSQLiteのマネージド版なので、前提を確認したい方はこちらから。

SQLiteを本番DBに使う判断基準|「書き込み直列化」は弱点ではないSQLiteの「書き込みが同時1本に直列化される」制約が本番でどう効くかを解説します。ロックの正体と待ち時間の実感値、ECを例にした思考実験、唯一の事故パターンである長いトランザクション、在庫競合がむしろ単純になる理由、採用判断の目安までを整理します。www.tentspace.net

Cloudflareの料金は「使った分だけ、ただし課金軸の定義に癖がある」というパターンが多く、Durable Objectsのときも請求の本体は別の場所にありました。コスト設計の考え方はこちらの記事も同じ軸で書いています。

Durable Objectsの高額請求は怖くない|AIチャットのコストの本体はトークン代Cloudflare WorkersでSaaSにAIチャットを組み込むときの費用の見方を整理します。Durable Objectsの課金を実際に計算すると月16万チャットまで無料枠に収まり、請求の本体はモデルのトークン代でした。家計簿サービスの設計検討で出た試算とプラン逆算をまとめます。www.tentspace.net

料金表の数字を眺めて「安い」と言うだけなら誰でもできます。効いてくるのは課金軸の定義まで読むこと。D1なら「DB数は数えなくていい、スキャン行数だけ数えろ」——これが今回の結論です。