Cloudflare D1 的迁移怎么做?
朴素的做法有两种:在控制台手动改表,或者在应用启动时 CREATE TABLE IF NOT EXISTS。前者不可复现,后者在多实例并发时不可控。
D1 的答案是迁移文件:用 wrangler d1 migrations 的 create、list、apply 三条命令,把 schema 当作有序的 SQL 文件来管理。 已应用的记录保存在数据库里,本地和线上各是一份独立的数据。
下面按你实际会遇到的顺序,讲命令、配置、本地与线上的差别,以及写迁移的原则。
迁移的三条命令分别做什么?
wrangler d1 migrations create <DATABASE_NAME> init
wrangler d1 migrations list <DATABASE_NAME> --local
wrangler d1 migrations apply <DATABASE_NAME> --localcreate 生成带版本号的空 SQL 文件,list 显示未应用的迁移,apply 依次执行。我在本地跑过 apply --local,之后 list 显示该文件状态为已应用。
已应用的记录存在哪?
保存在数据库里的 d1_migrations 表中,这是默认值,可以用 migrations_table 修改。迁移文件所在目录默认是 migrations/,可以用 migrations_dir 修改。
{
"name": "app",
"main": "src/index.ts",
"compatibility_date": "2025-09-01",
"d1_databases": [
{
"binding": "DB",
"database_name": "app-db",
"database_id": "<填入你的 database_id>",
"migrations_dir": "migrations"
}
]
}配置里的 binding 怎么写、怎么在 Worker 里用,见 Wrangler 与 bindings。
命令里该用数据库名还是绑定名?
用数据库名。官方的建议是迁移命令使用数据库名而不是绑定名,因为绑定名可能被改,造成迁移打到错误的库。
另外,本地和线上是两份独立数据。线上要加 --remote,并且需要你自己确认目标是对的。
迁移文件怎么写才安全?
我的原则只有两条:
- 迁移文件只增不改,已上线的文件不要再编辑;
- 破坏性变更拆成多步:加新列、回填、切换读、删旧列。
涉及外键违例的结构变更,官方给出的做法是先 PRAGMA defer_foreign_keys = true。
本地开发用 --local 的独立数据,不要假设它和线上数据一致。迁移上线前,先在本地与预发环境各跑一遍。
本文里 apply --local 之后 list 显示已应用,是我在本地 wrangler/miniflare 上看到的。默认表名、默认目录、用数据库名的建议和外键的 PRAGMA 做法来自官方文档,我没有逐项验证。命令与行为会变化,以 2026-10 的官方文档为准。