数据与配置迁移

评估表结构、配置、资源和业务标识的变化,为已有项目设计可验证的迁移路径。

模板源码能够编译,不代表原项目的数据可以直接使用新版代码。本篇讨论未来更新涉及结构或配置变化时需要检查的内容。没有这些变化时,不需要额外创建迁移文件。

一、先列出真正发生的变化

变化需要的处理
只修改页面文案或不影响接口的样式通常不需要数据库迁移
新增可选配置项检查默认行为、Schema 和调用方
配置字段改名或类型变化同步实际配置、Schema、读取代码和诊断脚本
新增列、约束、索引或状态设计增量 SQL,检查旧数据是否满足要求
产品、角色、权益等 Key 改名同时检查配置、代码与持久化记录
Queue、Event 或 Command 数据格式变化检查等待执行的消息和数据库中的旧执行记录
API 或认证规则变化检查调用方、已有会话、令牌和失败响应

“新增字段”也可能造成不兼容。例如新增必填字段但没有默认值,现有配置或旧数据就可能无法通过校验。是否不兼容,应以已有使用方式能否继续工作判断。

二、数据库基线与迁移各做什么

当前模板有两套基线:

Binding基线文件迁移命名
DBschema/db-init.sqlNNNN-db-description.sql
ANALYTICS_DBschema/analytics-init.sqlNNNN-analytics-description.sql

迁移文件位于 schema/migrations,四位编号按执行顺序安排,描述使用小写字母、数字和连字符。自己的项目已经有迁移时,要先核对编号、文件名与执行历史,不能直接把上游文件覆盖进同名位置。

当前迁移脚本会对空数据库执行基线,然后应用匹配的迁移。已有数据库存在相应基线表时,只应用尚未执行的迁移。非空库缺少 saas_user 或 analytics_session 时会停止,要求先检查数据库来源。

基线文件包含删除旧表的语句,不能用重跑基线升级已有数据库。修改基线也不会自动改变已上线的表结构。

同时验证旧库和空库

如果把某个新增列直接写进新基线,又保留一条无条件添加同名列的迁移,空库初始化就可能重复添加。当前脚本不会自动判断某个基线已经包含哪些历史变更。

因此,涉及基线调整时必须分别验证:

  1. 从当前项目真实旧结构升级,保留原有数据。
  2. 从空数据库初始化,再应用迁移,能够得到可用结构。

不要通过删除迁移记录或随意标记“已执行”让两条路径看起来成功。需要调整基线策略时,应明确设计,并保留可复现的测试结果。

三、从业务规则设计迁移

编写 SQL 前,先回答:旧代码读取和写入什么,新代码需要什么,历史数据缺少的值从哪里来。

例如某个业务表需要增加可空备注,可以先增加可空列,再让新代码使用它。如果要增加非空约束或唯一约束,必须先确认已有记录满足条件,不能只在空库上验证 SQL。

表重建还要保留主键、外键、索引、默认值和状态约束。不要只复制列名,遗漏的索引或约束可能在上线后才表现为慢查询或重复数据。

已经在生产或其他共享环境执行过的迁移应保留历史,需要修正时新增后续迁移。迁移是否执行由目标库记录,不能用本地文件存在来推断线上已完成。

具体查询和故障处理见数据库与迁移排错。D1 的迁移机制见 Cloudflare D1 migrations。

四、验证迁移而不接触真实业务

准备隔离的测试数据,结构应代表升级前版本。包含空值、历史状态、关联记录和已使用过的业务数据,不应只有新建空表。

确认命令面向本地测试状态后执行:

npm run db:migrate:local

检查迁移记录、最终结构、关键记录数量和业务关联,再使用应用读写这些数据。对权限和支付相关变化,还要检查重复执行是否重复授予、状态是否错误回退。

该命令会处理两个本地数据库。如果业务库成功、统计库失败,不能认为前面的变化一起撤销。生产迁移也不能视为跨两个数据库的一次事务。

需要试验空库时,另建隔离的本地环境,不要清空正在使用的开发数据库来凑齐验证步骤。

五、保持发布前后的兼容关系

当前 deploy:update 先运行远程迁移,再发布新 Worker。这意味着 SQL 执行后,旧代码仍可能继续处理请求。

新增结构通常比立即删除旧结构容易安排。例如确实需要替换一个已有字段时,可以评估分阶段发布:先准备新结构并让代码适配,再完成数据转换,确认无旧引用后清理旧结构。

这只是有实际迁移需要时的发布方案,不要求每次更新都增加兼容层。每个阶段都应有完成条件,临时读取逻辑和旧字段要有明确清理安排。

如果旧 Worker 无法读取新结构,就不能宣称“发布失败时直接切回旧代码即可”。应重新设计迁移顺序,或为该变更制定单独的受控切换方案。

六、同步配置,但保留项目身份

对照以下位置处理配置变化:

  • config/ 中的项目值与 src/libs/config/schemas/ 中的校验。
  • 服务端与客户端配置导出,以及页面、API、Service 和脚本调用方。
  • example.vars 与实际的 .env、.env.production。
  • wrangler.jsonc 中的绑定与 scripts/doctor/ 中的检查规则。

不要用新版模板的品牌、管理员邮箱、产品或资源名称覆盖项目值。新增配置字段时,先理解它的默认效果,再决定本项目是否启用。

新增 Secret 应通过原有安全方式配置。SAAS_SECRET 必须保留项目已有值,它不是升级时应该重新生成的版本标识。

七、环境变量和 Cloudflare 资源

example.vars 只是变量模板,不会自动把新字段合并到现有环境文件。变量改名后,需要同步读取代码、检查脚本、环境文件和部署行为。

以 VITE_ 开头的变量可能进入浏览器产物,只能保存公开值。删除 .env.production 的某一项或将它留空,也不会自动删除 Worker 已有的远程 Secret,应检查线上实际使用情况后单独处理。

Binding 变化后运行:

npm run cf-typegen
npm run doctor

生产发布准备阶段再运行 npm run doctor:remote。这些检查不负责自动建立所有缺失资源,也不能代替真实资源核对。

资源变化特别检查
D1、KV保留原项目资源 ID,明确数据是否需要搬迁
R2MAIN_R2.bucket_name 与 R2_BUCKET_NAME 一致,原文件仍可访问
Queue生产者、消费者与 *_QUEUE_NAME 一致,旧消息格式仍可处理
Durable Object导出类名、Binding 和迁移声明,不能覆盖既有迁移历史
CronWrangler 表达式与 src/entry/index.ts 的字符串分发条件一致

新增远程资源属于单独的实施步骤。不能假定 cf-typegen 会创建资源,也不能为已有项目重新执行首次部署来代替迁移设计。

八、把业务 Key 当作数据标识检查

产品 ID、套餐 ID、角色、权益、能力和 Scope 都可能写进数据库或外部系统。修改 Key 不只是改显示文案。

重命名前搜索定义、引用、数据库保存值和事件载荷。比如删除旧 Price 的套餐映射,可能影响旧订单回调和历史订阅处理。更换 STRIPE_CONNECTION_ID 也会改变支付记录的识别范围。

Queue 格式变化时,还应检查 event_execution、command_execution 中的旧版本和载荷。仅升级新请求的创建代码,不能保证升级前排队的任务仍然能执行。

本次没有明确需要时,保留稳定标识。确需迁移时,把转换范围、旧记录如何继续处理和重复执行的行为写入变更计划。

九、提前确认恢复材料

数据保护范围应覆盖这次真正会改变的内容。D1 备份不包含 R2 文件、KV 内容、Queue 消息、Secret 或 Stripe 中已经发生的交易。

为未来生产迁移准备数据库导出或可用恢复点,并验证恢复方法。D1 提供 Time Travel,但应在操作时确认账户可用窗口与目标恢复点,不能假定任意历史时间都可恢复,参见 Time Travel 与备份。

恢复数据库还可能丢失恢复点之后的合法写入,因此需要核对这段时间的新用户、付款和后台操作。它不是普通代码回滚的自动附带步骤。

完成这些准备后,再进入验证、发布与维护记录。