切换主题

一人公司数据库怎么选:D1、Postgres、R2、S3、SQLite

Easton editorial illustration: central four-way data-routing hub, structured relational database cylinder, object-storage bucket holding file sheets, single local database disk, sealed backup archive box

"Cloudflare D1 官方价格页说明 rows read/written、存储额度、索引对扫描行数的影响,以及 Free 达到日限额后的行为。"

你的项目里有这些数据对象:users 表、orders 订单、usage_events 日志、uploads/2026/06/report.pdf 用户上传文件、cache:daily-stats 缓存、local-dev.sqlite 本地开发数据。它们应该分别放进哪个存储?把业务事实塞进对象存储,几天后备份、查询和导出都变慢。用 D1 存数据却没有为过滤条件建索引,rows read 会快速消耗额度;Paid 方案还会产生超额费用。本文建立数据类型→存储选项的决策表,帮你判断每类数据放哪里,避开常见选型错误。

数据类型决策表:你的数据应该放哪里

一人公司项目里的数据对象类型不同,存储选择也不同。业务事实需要查询、关联和权限控制,事件日志需要高频写入和低成本,对象文件需要零出站费用和 CDN 加速。判断依据是:是否需要结构化查询、是否需要权限控制、访问频率、出站成本敏感度。

以下决策表帮你判断每类数据应该放进哪个存储。

7类数据类型和存储选项

数据类型具体对象推荐存储判断依据
业务事实users 表、orders 订单、订阅权益、支付记录Supabase Postgres(优先)或 D1需要结构化查询(SELECT/JOIN)、权限控制(RLS)、与方案匹配的备份/时间点恢复、审计追踪
事件日志usage_events 日志、操作记录、访问统计D1(边缘读写)或 Postgres(审计需求)高频写入、查询简单;非审计事件可降低恢复优先级,安全/计费事件仍需可靠保留
对象文件uploads/2026/06/report.pdf 用户上传、生成结果、导出文件、图片R2(Cloudflare 生态)或 S3(AWS 生态)或 Supabase Storage不进数据库 blob;零出站成本(R2)、成熟生态(S3)、与 Auth/Postgres 集成(Supabase Storage)
缓存cache:daily-stats 缓存、短期状态、临时计算结果Cloudflare KV、D1、或本地 SQLite读写频繁、通常可过期或重建;边缘轻量用 KV/D1,本地开发用 SQLite
本地开发数据local-dev.sqlite 本地测试数据、单用户工具后台SQLite(本地文件)单用户、无权限系统、copy a file and run、删除摩擦小
边缘轻数据Workers 工具站计数器、配置表、域名映射D1 或 Cloudflare KVWorkers 原生访问、轻关系数据、边缘读多
备份数据库备份文件、导出快照R2/S3 + 本地副本零出站成本(R2)、合规和生命周期(S3)、本地副本防止平台故障

业务事实优先 Postgres 的原因

业务事实(users、orders、订阅权益、支付记录)是项目核心数据,丢失会影响权益和收入。这类数据需要:

  • 结构化查询:SELECT、JOIN、WHERE、ORDER BY 这些 SQL 操作在关系数据库里高效实现,对象存储无法提供。
  • 权限控制:Supabase Postgres 的 RLS(Row Level Security)让数据库可从 client 安全查询,D1 目前缺少原生 RLS。
  • 备份和恢复:Supabase Pro、Team 和 Enterprise 有日备份,PITR 需另行启用付费附加项;D1 Time Travel 在 Workers Free 保留 7 天、Paid 保留 30 天。
  • 审计追踪:支付订单、订阅权益变更需要审计日志,Postgres 的触发器和审计表更容易实现。

Supabase Postgres 不是 Postgres abstraction:每个 Supabase project 是完整的 Postgres database,Auth、Storage、Realtime、Edge Functions 都建在数据库之上(Supabase Database 官方文档)。这意味着你可以用完整的 Postgres 能力,而不是受限的 subset。

事件日志可分离存储

事件日志(usage_events、操作记录、访问统计)和业务事实不同:

  • 写入频率高,但查询需求简单(时间范围查询、聚合统计)。
  • 非审计型分析事件通常可以采用较低的恢复优先级,但仍应明确保留期和丢失容忍度;安全、计费和权益事件不能按普通访问统计处理。
  • 成本敏感:高频写入会消耗 rows written 或 storage quota。

判断边界:

  • 如果需要审计追踪(例如操作记录需要关联 user_id 和 action),优先 Postgres。
  • 如果只是计数器或统计日志(例如页面访问计数),D1 或 KV 更合适。

对象文件不进数据库

用户上传文件(uploads/2026/06/report.pdf)、生成结果、导出文件、图片不应该放进数据库 blob 字段。原因:

  • 备份成本:数据库备份会包含所有 blob,备份时间和存储成本增加。
  • 查询负担:把大对象和关系数据混在一行,会放大传输、缓存、备份和维护成本,也更容易让宽查询意外带回文件内容。
  • CDN 加速:对象存储更容易接入 CDN、缓存策略和签名下载;数据库 blob 通常需要应用层转发。
  • 出站成本:从数据库读取 blob 会占用数据库与应用带宽;R2 直接向互联网传输时不收 R2 egress 费用,S3 则按对应区域和目的地计费。

正确做法:数据库只存对象 key(例如 uploads/2026/06/report.pdf),对象文件存进 R2/S3/Supabase Storage。

缓存和本地开发数据

缓存(cache:daily-stats、session、临时计算)的特征:

  • 读写频繁,通常允许过期或重建;登录 session 等安全状态仍要单独定义一致性、过期和撤销要求。
  • 可重建缓存通常不需要进入业务数据库备份;安全 session、撤销记录等状态要按其风险单独设计持久化。
  • 边缘访问时延迟敏感。

推荐:

  • 边缘轻量缓存:Cloudflare KV 或 D1(Workers 原生访问)。
  • 本地开发缓存:SQLite 文件或内存 cache。

本地开发数据(local-dev.sqlite)是单用户、无权限系统的场景:

  • SQLite 官方定位:local data storage、应用文件、本地数据(SQLite When to use)。
  • 不直接竞争 client/server DB:SQLite 强调 local、single-user,Postgres 强调 shared repository、multi-user。

边缘轻数据和备份

边缘轻数据(Workers 工具站计数器、配置表)优先 D1 或 KV:

  • Workers 原生访问:HTTP API 或 Workers SDK 直接访问,无额外 latency。
  • 轻关系数据:简单的表结构,不需要复杂 JOIN 或外键。
  • 边缘读多:大部分查询是读取,写入较少。

备份(数据库备份文件、导出快照)需要零出站成本和本地副本:

  • R2 free egress 降低备份下载成本。
  • S3 提供合规能力(Object Lock、Glacier tier)和生命周期管理。
  • 本地副本防止平台故障:例如 Cloudflare outage 时可以从本地 SQLite 恢复。

D1:Workers 原生的边缘数据库

D1 是 Cloudflare managed serverless database,具备 SQLite SQL semantics(D1 overview)。核心特性:

  • Time Travel:Workers Free 可恢复到最近 7 天内的任一分钟,Workers Paid 为 30 天;恢复会原地覆盖数据库,操作前要确认目标时间点。
  • read replication:降低读延迟和扩展读吞吐,适合边缘读多场景。
  • Workers/HTTP API access:Workers SDK 或 HTTP API 直接访问,无需额外数据库连接池。
  • built-in disaster recovery:Cloudflare 自动处理数据库故障和备份。

适用场景:Workers 原生和边缘读多

D1 适合以下场景:

  • Workers/Pages 原生项目:已经在 Cloudflare Workers 或 Pages 上部署,D1 是原生边缘数据库,无需跨平台访问。
  • 轻关系数据:简单的表结构(例如配置表、计数器、域名映射),不需要复杂 JOIN 或外键。
  • 边缘读多写少:大部分查询是读取,写入较少;read replication 可以扩展读吞吐。
  • Cloudflare 生态优先:项目已经使用 Workers、R2、KV、Vectorize,D1 是数据库层补充。

不适合场景:

  • 多用户 SaaS 需要权限系统(RLS):Supabase Postgres 更合适。
  • 支付订单和订阅权益:需要成熟的约束、审计和可验证恢复流程,通常优先 Postgres;具体恢复窗口取决于所选托管方案和附加项。
  • 审计追踪需求:Postgres 触发器和审计表更成熟。

rows read 计费机制:扫描行数而非返回行数

D1 按 rows read、rows written 和 storage 计费,这是和传统数据库不同的计费方式。rows read 计费方式:rows read 是查询扫描的行数,不是返回的行数。

截至 2026-06,D1 pricing 显示(价格易变,发布前复核):

项目Free 版Paid 版(Workers Paid)
rows read/day5M包含 25B/month
rows written/day100k包含 50M/month
storage5GB total5GB included,超额 $0.75/GB-month
rows read 超额计费-$0.001/million rows read
rows written 超额计费-$1/million rows written
egress/bandwidth无额外 charge无额外 charge

rows read 计费陷阱:一个工具站 SELECT * FROM orders WHERE user_id = X LIMIT 20,如果没有在 user_id 上创建索引,D1 会扫描全表(例如 50K 行)来找到 20 行返回。rows read = 50K,而不是 20。

一个未索引过滤即使只返回少量结果,也可能扫描整张表;实际消耗应查看每次查询返回的 meta.rows_read,不要用返回条数推算。

索引设计和查询效率

解决 rows read 陷阱的方法:

  1. 创建索引:CREATE INDEX idx_user_id ON orders(user_id); 让 D1 只扫描满足索引条件的子集,具体 rows read 以每次查询返回的 meta.rows_read 为准。
  2. 缩小查询范围:只取需要的列有利于减少返回载荷和意外耦合,但 D1 的 rows read 按扫描行数计数,列数不会改变行数计费;降低 rows read 的关键仍是索引和过滤条件。
  3. 监控 rows_read 与返回结果的比例:D1 会在查询 meta 和控制台指标中提供 rows read / written,可自行计算查询效率,定位全表扫描。

索引设计建议:

  • 在 WHERE 条件列上创建索引(例如 user_id、created_at)。
  • 在 JOIN 条件列上创建索引(例如 order_id、product_id)。
  • 避免过多索引:索引会增加 rows written(每次 INSERT/UPDATE 都需要更新索引)。
  • 定期审查慢查询:D1 console 会显示 rows read 较高的查询。

D1 免费额度和成本判断

D1 Free 版的限制:

  • 5M rows read/day:如果一个查询平均 rows read = 50K,每天只能执行 100 次。
  • 100k rows written/day:写入密集场景会快速消耗额度。
  • 5GB storage:业务事实数据量较大时会接近上限。

何时升级 Paid 版:

  • rows read/day 接近或超过 5M:Free 达到日限额后查询会报错;Paid 改为每月包含 25B rows read,超额计费。
  • rows written/day 超过 100k:Paid 包含 50M/month。
  • storage 超过 5GB:超额按 $0.75/GB-month 计费。

D1 适合边缘轻数据,不适合高频写入或大存储场景。如果你的工具站数据量增长快,需要监控 rows read/written 和 storage 使用率。

Supabase Postgres:BaaS 默认选择

Supabase 不是 Postgres abstraction,每个 project 是完整的 Postgres database(Supabase Database 官方文档)。Auth、Storage、Realtime、Edge Functions 都建在数据库之上,这意味着你可以用完整的 Postgres 能力:复杂查询、外键、触发器、事务、MVCC、extensions。

RLS:让数据库可从 client 安全查询

Supabase Postgres 的核心能力是 RLS(Row Level Security)。传统做法是数据库只允许 server 访问,client 通过 API server 间接查询。RLS 让数据库直接处理权限逻辑,client 可以安全查询,权限控制、审计追踪和 API 代码减少都在数据库层完成。

RLS 的作用:

  • 权限控制:定义 policy 让 user_id = current_user_id 的行可以被查询,其他行不可见。
  • 审计追踪:RLS 负责访问边界;若要追踪谁在何时做了什么,还需要单独的审计表、触发器或日志方案。
  • 减少重复权限代码:权限规则可下沉到数据库层,但服务端仍需验证身份、控制密钥并审查高权限操作。

适用场景:业务事实、权限强相关、多用户 SaaS、支付订单、订阅权益。这些数据需要权限控制和审计追踪,RLS 是原生解决方案。

备份和恢复

截至 2026-07,Supabase 官方当前口径如下(价格和方案易变,发布前仍应复核):

项目Free 版Pro 版Team 版
database size每项目含 500MB每项目含 8GB,超额计费每项目含 8GB,超额计费
price$0$25/month$599/month
automatic backups不包含日备份,保留 7 天日备份,保留 14 天
PITR不包含付费附加项,7 天保留从约 $100/month 起付费附加项,7 天保留从约 $100/month 起

这意味着:

  • Free 项目不能把平台自动备份当成恢复方案,应定期用 supabase db dump 或 pg_dump 导出并保留异地副本。
  • Pro 和 Team 提供日备份,但日备份最多仍可能损失一天内的变更。
  • PITR 不是 Pro 默认包含项;它需要至少 Small compute,并按 7、14 或 28 天保留窗口另行计费。

是否升级不能只看数据库容量。订单、订阅权益等业务事实更需要先定义恢复点目标、恢复时间目标和演练频率,再决定日备份是否足够、是否需要 PITR。

与 D1 的对比:权限和备份差异

项目D1Supabase Postgres
定位Workers 原生边缘数据库BaaS 默认 Postgres
权限控制无原生 RLSRLS 让数据库可从 client 安全查询
备份Time Travel:Free 7 天、Paid 30 天Pro/Team/Enterprise 日备份;PITR 为付费附加项
适用场景边缘轻数据、Workers 原生业务事实、多用户 SaaS、支付订单、订阅权益
计费rows read/written + storagedatabase storage + MAU

组合使用场景:

  • D1 做边缘读多缓存:配置表、计数器、域名映射。
  • Supabase Postgres 做业务事实:users 表、orders 订单、订阅权益、支付记录。

例如:一个 SaaS 工具站,Workers 原生访问 D1 存配置和计数器,Supabase Postgres 存用户和订阅权益。D1 边缘低延迟,Postgres 权限和备份能力完整。

R2:零出站成本的对象存储

R2 是 Cloudflare 的 S3-compatible object storage,主要优势是直接从 R2、Workers API、S3 API 或 r2.dev 域名向互联网传输时不收 R2 egress 费用(R2 pricing)。连接其他计量服务时仍可能产生服务侧费用。R2 适合用户上传文件、生成结果和导出文件,但“免出站费”不等于存储与操作都免费。

Class A/B Operations 计费机制

R2 按 storage、Class A operations、Class B operations 计费,这是和传统对象存储不同的计费方式。

截至 2026-06,R2 pricing 显示(价格易变,发布前复核):

项目Free 版Standard storageInfrequent Access
storage10GB-month/month$0.015/GB-month$0.01/GB-month
Class A Operations1M/month$4.50/million$9.00/million
Class B Operations10M/month$0.36/million$0.90/million
Data RetrievalNoneNone$0.01/GB
Egress (Internet)FreeFreeFree
Minimum storage durationNoneNone30 days

Class A/B Operations 说明(R2 pricing):

  • Class A(昂贵):PutObject、CopyObject、ListObjects、LifecycleStorageTierTransition 等。写入操作和列表操作属于 Class A,单价 $4.50/million(Standard)。
  • Class B(便宜):GetObject、HeadObject、HeadBucket 等。读取操作属于 Class B,单价 $0.36/million(Standard)。
  • 免费操作:DeleteObject、DeleteBucket、AbortMultipartUpload。

R2 免费额度误解

常见错误:10GB-month free ≠ 无限存储。

  • storage quota:10GB-month 是存储空间限制,不是流量限制。如果你的文件总量超过 10GB,需要升级 Paid 或清理旧文件。
  • Class A Operations quota:1M/month 是写入操作限制,如果一个工具站每天上传 1000 个文件,一个月消耗 30K Class A operations,还在免费额度内;但如果批量写入(例如每天上传 10K 文件),会快速消耗额度。
  • Class B Operations quota:10M/month 是读取操作限制,如果一个图片 CDN 每天访问 100K 次,一个月消耗 3M Class B operations,还在免费额度内;但如果高频访问(例如每天 1M 次),会超出额度。

Infrequent Access 的 minimum storage duration:30 days minimum storage duration 意味着你不能在 30 天内删除文件,否则仍需要支付 30 天的存储费用。这个限制适合备份和长期存储,不适合临时文件。

适用场景:用户上传文件和生成结果

R2 适合以下场景:

  • 用户上传文件:uploads/2026/06/report.pdf、图片、文档。零出站成本降低 CDN 和下载费用。
  • 生成结果:导出文件、报告、图片处理结果。频繁下载场景,R2 free egress 是主要优势。
  • 备份:数据库备份文件、导出快照。备份下载需要零出站成本。
  • Cloudflare 生态优先:项目已经使用 Workers、D1、KV、Vectorize,R2 是对象存储层补充。

不适合场景:

  • 业务事实数据(users、orders):需要结构化查询和权限控制,对象存储无法提供。
  • 需要 S3 Object Lock、更多存储层级或跨桶/跨区域复制等 AWS 原生治理能力时,S3 更合适。

R2 vs S3 对比

项目R2S3
egress (Internet)R2 侧免费;其他计量服务可能收费按区域、目的地和用量计费,需查对应 AWS 价格表
storage$0.015/GB-month多 tier(Standard、Glacier、Deep Archive)
生态Cloudflare Workers/Pages 原生AWS Lambda triggers、成熟生态
治理能力生命周期、Standard/IA、Queues 事件通知;功能范围较聚焦Object Lock、多种 Glacier 存储类、生命周期管理、Replication 与多种事件目标
适用场景Cloudflare 生态、出站成本敏感AWS 生态、合规需求、数据湖、企业应用

何时选择 R2:

  • 出站成本敏感:用户上传文件和生成结果频繁下载,R2 free egress 降低成本。
  • Cloudflare 生态:Workers/Pages 原生访问,无需跨平台 latency。

何时选择 S3:

  • AWS 生态优先:Lambda triggers、数据湖、企业应用需要 S3 集成。
  • 治理需求:Object Lock、更多 Glacier 存储类、跨区域复制和 AWS 原生权限体系。
  • 成熟生态:AWS 工具链(CloudWatch、IAM、S3 Select、S3 Batch Operations)更完整。

R2 和 S3 不是完全替代关系。如果你的项目已经使用 Cloudflare Workers/Pages,R2 是对象存储的默认选择;如果需要 AWS 生态和合规能力,S3 更合适。组合使用场景:R2 存 CDN 文件和生成结果,S3 存备份和合规数据。

S3:AWS 生态和合规需求

S3 是 AWS object storage service,用于 data lakes、websites、mobile apps、backup/restore、archive、enterprise apps、IoT、big data analytics(Amazon S3 user guide)。核心特性:

  • storage classes:Standard、Glacier、Glacier Deep Archive、Intelligent-Tiering 等多种存储 tier,适合不同访问频率和成本需求。
  • lifecycle:自动迁移旧文件到 Glacier 或删除过期文件,降低存储成本。
  • Object Lock:WORM(Write Once Read Many)模式,合规场景防止文件被修改或删除。
  • Replication:跨区域复制,灾难恢复和低延迟访问。
  • IAM/bucket policies:细粒度权限控制,Block Public Access 防止意外公开。
  • Lambda triggers:文件上传触发 Lambda 函数,自动化处理。

适用场景:AWS 生态和合规需求

S3 适合以下场景:

  • AWS 生态优先:项目已经使用 Lambda、EC2、RDS、DynamoDB,S3 是对象存储层补充。
  • 合规需求:Object Lock、Glacier tier、SOC2 compliance、GDPR 合规。
  • 数据湖:大数据分析和 IoT 数据存储,需要 S3 Select 和 S3 Batch Operations。
  • 企业应用:备份/restore、archive、长期存储,需要生命周期管理。

不适合场景:

  • 出站成本敏感:用户上传文件频繁下载时,S3 费用取决于区域、目的地、缓存和用量,需要用当前 AWS 价格表估算。
  • Cloudflare 生态优先:Workers/Pages 原生访问 R2 更方便。

S3 vs R2:成熟生态和合规能力

S3 的优势是成熟生态和合规能力:

  • 事件目标:S3 Event Notifications 可发送到 SNS、SQS、Lambda 和 EventBridge;R2 也支持把对象创建/删除事件发送到 Cloudflare Queues,再由 Worker 或 HTTP pull 消费。
  • 存储层级:S3 提供多种 Glacier 归档类;R2 当前主要是 Standard 和 Infrequent Access。
  • Object Lock:S3 可用 WORM 模式防止对象在保留期内被覆盖或删除。
  • Replication:S3 提供同区域或跨区域的桶级复制,并可复制对象元数据和标签。

何时选择 S3:

  • AWS 生态优先:Lambda triggers、数据湖、企业应用需要 S3 集成。
  • 合规需求:Object Lock、Glacier tier、生命周期管理。
  • 长期存储:Glacier 和 Glacier Deep Archive 成本更低。

何时选择 R2:

  • 出站成本敏感:用户上传文件频繁下载。
  • Cloudflare 生态:Workers/Pages 原生访问。

组合使用场景:R2 存 CDN 文件和生成结果,S3 存备份和合规数据。一个项目可以同时使用两种对象存储,根据数据类型和访问模式选择。

SQLite:本地和嵌入式优先

SQLite 是 local data storage、应用文件、本地数据、低中流量网站、数据分析、缓存的选择(SQLite When to use)。官方定位:SQLite 不直接和 client/server SQL DB 竞争;client/server DB 强调 shared repository、scalability、concurrency、centralization、control;SQLite 强调 local、single-user、copy a file and run。

适用场景:单用户工具和本地后台

SQLite 适合以下场景:

  • 单用户工具:本地后台、数据分析工具、小游戏、单文件数据。
  • 应用文件:例如 CAD 软件、财务软件、媒体管理工具,数据文件和应用文件一起分发。
  • 低中流量网站:SQLite 官方提到 “fewer than 100K hits/day” 通常 fine(SQLite When to use),但这是保守估计,不作为本站性能承诺。实际承载能力取决于硬件、查询复杂度和并发写入。
  • 本地开发数据:local-dev.sqlite 测试数据,单用户、无权限系统。
  • 缓存和临时数据:session、临时计算结果,无需持久化。

不适合场景:

  • 多用户 SaaS:需要权限系统、并发写入、审计追踪,Postgres 更合适。
  • 支付订单和订阅权益:需要备份和 PITR,Postgres 时间窗口更长。
  • 高并发写入:SQLite 是单写入者多读取者,写入并发受限。

SQLite vs Postgres 判断:何时切换

项目SQLitePostgres
定位local data storage、应用文件shared repository、client/server DB
适用单用户工具、小游戏、本地后台多用户 SaaS、支付订单、订阅权益
并发单写入者,多读取者多写入者,MVCC
权限无用户管理RLS、用户/角色管理
成本无独立数据库服务器成本取决于自托管或托管方案、计算和备份配置

何时从 SQLite 切换到 Postgres(SQLite When to use + 社区实践):

  1. 用户数增长:从单用户工具变成多用户 SaaS,需要权限系统。
  2. 并发写入需求:多个用户同时修改数据,SQLite 单写入者会成为瓶颈。
  3. 支付需求:支付订单和订阅权益需要备份和审计追踪。
  4. 权限系统:users/roles/permissions 需要数据库层权限控制。

判断边界:如果一个工具站是单用户后台(例如你自己的数据分析工具),SQLite 是合适选择;如果变成多用户 SaaS(例如多个客户付费使用),需要切换到 Postgres。

SQLite 不是 Postgres 替代品

SQLite 官方定位明确:SQLite 不直接竞争 client/server SQL DB(SQLite When to use)。这意味着:

  • SQLite 强调 local、single-user、simplicity、copy a file and run、删除摩擦小。
  • Postgres 强调 shared repository、multi-user、concurrent state mutations、takes payments。

成本差异:SQLite 本身不需要独立数据库服务器,本地文件即可运行;Postgres 的成本取决于自托管或托管方案、计算规格、存储和备份配置。两者都需要可验证的备份与恢复设计。

何时选择 SQLite:

  • 本地工具和小游戏:单用户、无支付、无权限系统。
  • 本地开发数据:测试数据和临时文件。
  • 低中流量网站:个人博客、文档站、低并发写入。

何时选择 Postgres:

  • 多用户 SaaS:支付订单、订阅权益、权限系统。
  • 审计需求:操作记录和权益变更需要追踪。
  • 备份和 PITR:业务事实数据需要时间点恢复。

组合使用场景:本地开发用 SQLite 测试,生产环境用 Postgres;SQLite 做缓存和临时数据,Postgres 做业务事实。一个项目可以同时使用两种数据库,根据数据类型和访问模式选择。

常见错误警示

数据选型有三个常见错误:把业务事实塞对象存储、rows read 计费陷阱、R2 免费额度误解。这些错误会导致备份困难、查询变慢、成本失控。

错误1:把业务事实塞对象存储

users 表、orders 订单、usage_events 日志不应该放进 R2/S3。对象存储缺少结构化查询、关联查询、权限控制和审计追踪能力。

具体后果:

  • 无法 SELECT 和 JOIN:对象存储是 key-value 访问,无法执行 SELECT * FROM orders WHERE user_id = X LIMIT 20 或 JOIN users ON orders.user_id = users.id。如果需要查询订单,需要把所有订单文件下载到本地处理。
  • 备份困难:对象存储的备份是文件列表,无法按时间范围恢复(例如恢复最近 7 天的订单)。数据库备份是 SQL dump,可以按时间范围恢复。
  • 导出变慢:导出所有订单需要遍历所有对象文件,时间和带宽成本增加。数据库导出是 SQL query,可以按条件导出。

正确做法:数据库存业务事实(users、orders、订阅权益),对象存储存文件(uploads/2026/06/report.pdf、图片、备份)。数据库只存对象 key,对象文件存进 R2/S3/Supabase Storage。

错误2:rows read 计费陷阱

D1 的 rows read 计费是扫描行数,不是返回行数。无索引的 SELECT * 会导致 rows read 远大于返回行数,成本失控。

例如一个 50,000 行的 orders 表执行 SELECT * FROM orders WHERE user_id = ? LIMIT 20,如果 user_id 没有索引,D1 可能需要扫描大量行才能返回 20 行;实际消耗应以查询 meta.rows_read 为准。Free 达到 5M rows read 日限额后,查询会报错,Paid 超过月包含量则按量计费。

解决方案:

  1. 在真实过滤列上建立索引,例如 CREATE INDEX idx_user_id ON orders(user_id);。
  2. 用 EXPLAIN QUERY PLAN 和 meta.rows_read 检查查询是否仍在全表扫描。
  3. 只查询需要的列以控制载荷,但不要误以为列数会改变 rows read;D1 按扫描行数计数。

错误3:R2 免费额度误解

R2 Free 版的 10GB-month storage、1M Class A operations、10M Class B operations 不是无限使用。

具体误解:

  • 10GB-month ≠ 无限存储:storage quota 是空间限制,不是流量限制。如果你的文件总量超过 10GB,需要升级 Paid 或清理旧文件。
  • Class A operations ≠ 无限写入:PutObject 是 Class A operation,单价 $4.50/million(Standard)。如果一个工具站每天上传 10K 文件,一个月消耗 300K Class A operations,还在免费额度内;但如果批量写入(例如每天上传 100K 文件),会快速消耗额度。
  • Infrequent Access 30 days minimum:你不能在 30 天内删除文件,否则仍需要支付 30 天的存储费用。这个限制适合备份和长期存储,不适合临时文件。

正确做法:监控 storage、Class A/B operations 使用率,定期清理旧文件,避免批量写入消耗 Class A quota。如果需要临时文件存储,用本地 SQLite 或 KV 缓存,不要用 R2 Infrequent Access。

结论

一人公司项目的数据选型,判断依据是数据类型:业务事实、事件日志、对象文件、缓存、本地开发数据、边缘轻数据、备份。业务事实优先 Supabase Postgres(RLS、关系约束、可配置备份与审计),边缘轻数据优先 D1(Workers 原生、边缘读多),对象文件优先 R2/S3(零出站成本或合规需求),本地开发数据优先 SQLite(单用户、无权限系统)。

核心行动建议:

  1. 根据数据类型选择存储:参考数据类型决策表,判断每类数据的查询需求、权限控制、访问频率、成本敏感度。
  2. 索引设计降低成本:D1 rows read 是扫描行数,创建索引可以减少 rows read,避免成本失控。
  3. 避免常见错误:业务事实不塞对象存储、SELECT * 无索引、R2 免费额度误解。

后续会单独写支付方案和用户系统,这两篇文章会影响数据库选择:支付订单数据优先 Postgres(关系约束、备份和审计),用户权益和权限数据优先 Postgres(RLS 与可验证恢复)。如果你已经在规划支付和用户系统,可以提前考虑数据库选型。

如果你不确定某个数据对象应该放进哪个存储,可以参考数据类型决策表或系列上游文章(技术栈设计原则、后端技术栈、部署方案),建立整体技术架构后再细化存储选择。

为一人公司项目分配数据存储

从数据盘点到恢复演练,用六步确定数据库和对象存储边界。

  1. 1

    步骤 1: 列出数据对象

    把 users、orders、usage_events、uploads、cache、本地文件和备份逐项列出,不先按产品分组。
  2. 2

    步骤 2: 标记数据类型

    为每项标记业务事实、事件、对象文件、缓存、本地数据或备份,并注明是否允许过期或重建。
  3. 3

    步骤 3: 写清一致性与权限

    记录事务、约束、RLS、并发写入、审计和多实例共享需求,判断是否需要 Postgres 或 D1。
  4. 4

    步骤 4: 估算访问与成本触发器

    估算 D1 rows read/written、R2 Class A/B operations、对象容量、读取频率和潜在出站费用。
  5. 5

    步骤 5: 设计稳定引用

    数据库保存对象 key、owner、status 和 metadata,文件本体放对象存储,并保持 key 命名稳定。
  6. 6

    步骤 6: 验证备份和迁移

    设置导出、异地副本、删除窗口和恢复演练;迁移时先同步 metadata,再同步对象并切换读路径。

常见问题

一人公司数据库应该先选 D1 还是 Supabase Postgres?
Workers/Pages 原生、关系简单且读多的轻数据可先评估 D1;用户、订单、订阅、权限和复杂查询通常优先 Supabase Postgres。
R2 和 S3 适合放什么?
两者都适合图片、PDF、导出包、备份和生成结果等对象文件。数据库保存 metadata、owner、status 和 object key,不保存大文件本体。
SQLite 能不能直接做第一个 SaaS?
单机工具、低并发写入的小站可以使用 SQLite;一旦需要多实例共享、复杂权限、支付权益或大量并发写入,应优先评估 Postgres。
用户上传文件能不能放数据库?
通常不建议。文件本体放 R2、S3 或 Supabase Storage,数据库只保存引用和权限信息,备份、下载、CDN 与删除策略会更清楚。
D1 的 rows read 计费是什么意思?
rows read 统计查询扫描的行数,不是返回行数。未索引过滤可能返回很少结果却扫描大量行,应通过索引、EXPLAIN QUERY PLAN 和 meta.rows_read 验证。
Cloudflare R2 免费额度够不够?
要同时估算 Standard storage、Class A/B operations 和读取频率。当前免费层含 10 GB-month、每月 1M Class A 和 10M Class B,价格易变且不适用于 Infrequent Access。

25 分钟阅读 · 发布于: 2026年10月9日

评论

使用 GitHub 账号登录后即可评论

Easton BlogEaston Blog