数据库
了解 01MVP 的 D1、SQLite、Drizzle schema、migration 和多运行时适配方式。
01MVP 使用一套 Drizzle SQLite schema。生产数据库是 Cloudflare D1,本地开发和未来国内自托管都保持 SQLite 语义。
运行方式
| 运行位置 | 数据库访问方式 |
|---|---|
| Cloudflare Workers | DB D1 binding,Worker 内直接访问 |
| Zeabur / Docker / Node | 受 D1_PROXY_TOKEN 保护的 D1 binding 代理 |
| 本地开发和 E2E | Wrangler 管理的本地 SQLite 数据 |
| 国内自托管 | 普通 SQLite 适配器、持久卷和备份 |
业务代码统一调用 getDb(),不直接依赖某个部署平台。
为什么没有使用 PostgreSQL
当前产品的数据量、访问量和写入并发不需要 PostgreSQL 的扩展、复杂事务或高并发写入能力。使用 D1 可以减少独立数据库服务、连接池和备份设施,也能把应用与数据统一放到 Cloudflare。
这不是说 PostgreSQL 不好,而是现阶段没有必要为未发生的需求支付复杂度。出现下面任一条件时,再做迁移评估:
- 持续的高并发写入已经成为实际瓶颈。
- 必须使用 PostGIS、全文检索扩展或其他 PostgreSQL 专属能力。
- 分析型复杂查询超出 D1 的适用范围。
- 业务正确性必须依赖行级锁、特定隔离级别或 PostgreSQL 专属事务语义。
默认不要让 SQLite 和 PostgreSQL 同时充当主数据库。双写会带来一致性、补偿、迁移和排障成本。确实需要搜索、分析等专用系统时,可以从 SQLite 主库通过事件或异步任务同步派生数据,但不要让两个数据库共同决定同一份业务事实。
Schema 与数据表示
Schema 位于 products/01mvp/packages/db/src/schema/:
- 时间使用 Unix 毫秒整数。
- 布尔值存为
0/1,由 Drizzle 映射为boolean。 - JSON 存为文本,由 Drizzle 负责序列化。
- 金额和积分等需要精确表示的值使用十进制文本,避免浮点误差。
应用 SQL 不使用 ILIKE、PostgreSQL cast、advisory lock 等专属语法。跨语句原子操作优先使用 D1 batch、条件更新和经过审查的 SQLite trigger。
常用命令
修改 schema 后先生成 migration:
vpr @01mvp/product#db:generate应用到本地 D1:
vpr @01mvp/product#db:migrate应用到生产 D1:
vpr @01mvp/product#db:deploy生产 migration 必须先提交并审查,再在部署新代码之前执行。不要在应用启动时自动修改生产 schema。
查询示例
import { eq } from "drizzle-orm";
import { getDb, schema } from "@01mvp/db";
const db = getDb();
const rows = await db
.select()
.from(schema.user)
.where(eq(schema.user.id, userId));国内部署
D1 本身是 Cloudflare 服务,不能原样搬进国内服务器;可移植的是 SQLite schema、migration 和应用查询。国内部署时实现普通 SQLite 运行时适配器,并明确配置:
- 持久磁盘或持久卷。
- 定时备份与恢复演练。
- 单写节点,或能保证 SQLite 写入安全的部署拓扑。
- migration 在新版本上线前显式执行。
只要继续遵守 SQLite 可移植边界,业务层不需要因为部署地点改变而重写。
这篇文档有问题?