数据库备份与恢复
当前能力与范围
I15 新增完整数据库归档、校验清单与独立空库恢复,已在本地实际 PostgreSQL 和系统 pg_dump / pg_restore 验收。恢复核对账号、文件元数据、任务、账本、购买与迁移记录,文件对象检查点、独立空桶恢复和私有结果下载也已在受控的本地 R2 绑定验证;没有接入真实云端服务或自动为任意桶执行备份。正常预览仍为已授权的 0012,验收使用一次性数据库,不升级或覆盖主库。
原 bun db:export 默认只导出结构,适合查看 SQL,不能当作完整备份。本教程使用新增的 db:backup / db:restore。需要先安装 PostgreSQL 客户端工具并加入 PATH;备份客户端不能比源数据库更旧,具体版本兼容参见官方备份说明。核对日期:2026-10-11。
1. 选择来源并生成归档
开发来源沿用原数据库配置,测试与正式环境只从对应 .env.<环境>.local 读取 DATABASE_URL。环境文件必须存在并定义来源连接,不回退到开发或 shell 中旧地址。建议使用有完整读取权限的非池化直连;Hyperdrive 不是备份连接。
bun db:backup
bun db:backup:staging
bun db:backup:production选择一种命令对应自己的环境,不依次备份所有环境。--output 可指定本机私有目录,默认是 db/backups。当前执行这些命令仍是人工动作;持续集成、发布脚本和定时维护不会自动备份或恢复,也没有新增云端上传。
成功后输出目录 snapshot-<唯一编号>,内含:
| 文件 | 内容与边界 |
|---|---|
database.dump | 自定义格式完整数据库归档,包含数据、表结构、约束、序列及迁移记录;不包含 R2、队列消息、Worker Secrets 或集群角色 |
manifest.json | 格式版本、来源指纹、环境、时间、数据库版本、归档大小 / SHA-256,以及每张表的行数 / 数据摘要;不保存来源连接、密码、客户正文或对象路径 |
归档目录权限为 700,两个文件为 600。归档本身含用户数据和认证记录,保持私有存储;新站生成器排除备份目录、归档文件、已完成快照与临时目录。文件校验用于发现损坏,不证明归档来源可信;恢复自己的已确认归档。
备份在只读可重复读事务中导出快照,数据摘要与 pg_dump 使用同一快照,避免备份期间的积分 / 账本更新落入不同时间点。普通表、分区表与物化视图逐行排序、流式计算摘要;这需要额外扫描和排序,规模较大时应选择维护窗口并评估磁盘、执行时间和数据库负载。
没有按表筛选、增量或自动保留策略;人工清理命令见下文。失败归档不会发布为完成目录,并清理本次临时目录;进程被强制结束后可能留下 .partial-*,恢复命令不接受缺少完整清单的目录。单数据库快照不等于数据库、文件对象与外部支付共同事务。官方说明也区分数据库归档与集群级角色等对象。
2. 准备独立空目标
先准备独立恢复数据库及权限。不要把来源或正在提供服务的数据库作为目标;已有表、函数、类型、非默认扩展、对象或自定义模式都会拒绝恢复。命令不删除、清空或创建目标数据库。
根目录创建 .env.restore.local,只写目标连接:
RESTORE_DATABASE_URL=postgresql://restore_role:自己的密码@目标主机:5432/独立空库文件不提交。恢复命令只读取这个文件,忽略 shell 中的 RESTORE_DATABASE_URL、来源 DATABASE_URL 和开发环境回退。不要将完整连接写入命令参数;PostgreSQL 客户端接收连接环境字段,密码不进入参数列表,原始数据库错误也不输出。
3. 恢复并核对
bun db:restore --from backups/snapshot-自己的编号命令从数据库工作区解析相对路径,也可直接使用本机归档的绝对路径。按照备份命令实际输出的目录选择,避免猜测路径。
恢复顺序为归档 / 清单校验 → 来源与目标区分 → 空库检查 → 单事务恢复 → 表清单、行数与数据摘要核对。不会使用 --clean 或 --create。单事务执行依据官方恢复说明;恢复完成后的数据核对另行执行,若核对失败,目标仍需隔离,不标为验收完成。
恢复不复制原对象所有者及授权,避免把原角色安排直接带入新环境。之后需按自己的角色策略重新授予应用所需权限,由实际恢复 / 迁移所有者执行 db/scripts/grant-app-role.sql,见数据库角色与凭据;不能把恢复成功当作业务角色已具备权限。
4. 接入服务前检查
先保持目标隔离,不直接启动任务消费者、邮件发送或支付维护。
- 使用归档对应的模板版本核对迁移记录;新版本的增量迁移需另行检查,恢复命令不会自动升级。
- 按文件元数据核对私有对象是否存在、长度与内容摘要是否对应;同步准备对象备份 / 恢复。数据库里的
ready只说明历史状态,不证明目标桶现在有文件。 - 核对新环境的 Hyperdrive、R2、队列、域名、邮件地址、供应商模式和 Secrets;这些不在数据库归档中。
- 对恢复时间点之后的购买、退款、通知、计佣等外部事实另行对账,再决定何时恢复消费与定时维护,避免向用户报告过期结果。
- 在独立测试环境走注册、上传、处理、预览与下载,核对私有访问、账本、失败返还和恢复后的重复请求。
如需数据库与文件对象共同恢复,应先确定停写 / 停消费安排及共同检查时间,再备份数据库和对象;本阶段未提供原子跨服务快照、自动对象复制或云端故障切换。真实服务、备份保存 / 删除策略、恢复耗时与费用继续按部署验收记录。
文件对象检查点
新增 scripts/lib/file-backups.ts 的对象归档 / 恢复函数,当前用于受控 R2 绑定与实际本地验收。它们没有新增公开下载端点、后台任意桶复制入口、R2 云端凭据或自动运行计划;db:backup / db:restore 仍只处理数据库。
操作顺序是先停止来源写入、任务消费和清理,再完成数据库归档;保持来源暂停,将归档恢复到隔离空库并通过表数据校验,从这份冻结数据中取得全部 ready 文件引用。按冻结引用读取源对象,验证长度、SHA-256 和所需元数据,全部通过才发布对象检查点。这不是数据库和 R2 的原子事务,暂停来源仍是操作者的前置安排,函数不会替操作者停服务。
对象目录位于数据库归档的私有快照目录内,使用独立唯一编号:
| 内容 | 用途 |
|---|---|
file-manifest.json | 关联数据库归档 SHA-256、冻结文件清单摘要、捕获时间、文件编号、对象键、长度、内容摘要与类型;它含私有对象键,需保持私有 |
<文件编号>.blob | 原始文件内容,文件名从有效编号派生,避免把对象路径当成本机路径 |
归档目录为 700、文件为 600。当前单文件上限与模板一致为 16 MiB,引用最多 100,000 项、清单最多 64 MiB;按文件逐个读取,不将整个文件集合载入内存。数据库归档中的 externalObjectsIncluded: false 保持原义,文件内容在独立对象目录中,不改写旧数据库清单。
对象恢复要求文件检查点与指定数据库归档、目标冻结引用全部一致。先检查每个本地文件,缺失、损坏或换错清单时不写目标;再要求独立空桶,条件写入避免覆盖并发出现的对象,逐个读回核对。必要的类型、文件编号和摘要元数据恢复后,原私有文件接口仍按本站账号与所有权读取。
目标已存在对象时拒绝,不清空或覆盖。写入期间失败可能留下本次部分对象,目标需继续隔离;没有自动删除、断点修复或跨服务回退。该归档仅覆盖冻结的 ready 文件,不复制孤立对象、未就绪对象或队列消息;历史任务与外部付款状态仍按原维护和对账规则处理。
条件写入与对象元数据依据Cloudflare R2 官方说明,核对日期 2026-10-11;实际云端与 S3 传输、对象保留 / 删除策略和运行成本继续待验。
本机归档保留与清理
db:backup:prune 只维护指定本机目录,不读取数据库环境配置、不连接数据库或云服务,也不删除正在使用的 R2 对象。暂停该目录的备份、恢复、复制与其他清理操作,先查看预览:
bun db:backup:prune --directory backups --keep 7 --older-than 30路径从数据库工作区解析,可使用绝对路径。参数均需明确指定;--keep 至少为 1,--older-than 为正整数天数,以运行时间前的连续 24 小时计算。上例是用法,不是适用于所有产品的保留承诺。
规则按来源指纹和环境分别计算:始终保留每组最新的 7 份校验通过归档,以及完成时间在最近 30 天内的所有归档;只有同时超出数量且严格早于年龄界线的归档才列为 DELETE,恰好位于界线的仍保留。来源指纹沿用数据库归档的主机 / 端口 / 数据库,不包含密码。KEEP、DELETE、SKIP 分开显示,默认不修改任何文件。
只识别该目录直接下的 snapshot-<标准唯一编号>。逐一核对数据库清单、大小及完整 SHA-256,完成时间倒置或位于未来、损坏 / 缺失归档、符号链接、目录中的未知文件或未完成对象目录均跳过,不作为有效保留名额。其他目录与 .partial-* 留在原处,不打印这些未知条目的名称。
确认预览和保留安排后,再执行同样参数并追加 --apply:
bun db:backup:prune --directory backups --keep 7 --older-than 30 --apply执行时重新校验保留与待删除的全组归档和文件列表;发现变化则在开始删除前停止。删除以整个快照目录为单位,同目录下配套的私有对象检查点也一起删除,不留下只能恢复数据库的半份本机归档。被保留快照不会被改写;再次运行不重复删除已移除快照。
命令没有跨进程锁,重新校验也不是文件系统事务;其他操作必须保持暂停。删除期间的权限 / 磁盘错误可能已经移除部分快照,命令报告完整移除数后停止,需重新检查;不提供恢复已删除归档或安全擦除介质的承诺。没有定时清理、云端生命周期设置、跨账户副本或远程保留策略。
清理校验不执行 pg_restore,也不认证对象检查点内容完整,更不证明支付事实已对账。保留数量不能代替近期实际恢复验收;在决定期限前,先确认至少一份所需版本的完整归档可以在独立目标恢复,并检查配套文件与外部事实。云端保留 / 删除及实际存储费用仍按 I15 继续验收。
本地复验
bun db:validate-backups命令只接受回环 PostgreSQL 来源,创建自己的三个临时数据库,应用完整迁移并保存合成账号、文件、成功任务、积分账本与购买。真实备份期间并发写入,恢复后核对同一快照的全表摘要、积分 / 账本、迁移、序列、约束与外键;验收损坏归档、原库 / 已占用目标拒绝、私有文件权限、环境文件选择以及失败后的临时文件清理。实际恢复命令另在隔离目录执行,不创建正常项目的恢复环境文件。
验收结束删除本次数据库与归档,主预览不变。该命令没有恢复 R2 文件或调用供应商;云端完整恢复与 I16 的两站实际交付仍待验收。
另外运行文件与数据库共同恢复验收:
bun files:validate-backups命令创建两个自有回环数据库和两个独立本地 Worker / R2 目录,使用实际注册、上传、报价确认、扣积分和队列执行;任务花费 3 积分,成功后从 20 变为 17。归档并恢复数据库及输入 / 结果对象后,验证原用户下载真实结果、匿名和另一用户拒绝,重复原请求 / 定时维护不再次执行或扣费,实际重启仍可下载。受控破坏源对象和归档文件会拒绝完整检查点或在目标写入前停止;实际 R2 条件写入保留已有对象。
文件验收不会修改正常预览配置或数据库,结束删除自己的数据库、对象目录、归档与会话。主预览仍为 0012;这不是任意云桶的备份命令,也不代表真实云端故障切换已验收。
本机保留规则的实际数据库验收:
bun db:validate-retention创建两个自有回环数据库,源库完整迁移后生成三份真实 PostgreSQL 归档。命令预览不删除,非法策略拒绝;使用模拟的未来策略时钟让归档达到年龄条件,不改写真实捕获时间或归档内容。清理两份旧归档后,实际恢复保留的一份并核对全表摘要、迁移和 22 积分 / 账本;重复清理无新增删除。结束清理本次数据库与目录,正常主库和六项预览保留。这是本地清理验收,不把模拟年龄称为已保存多日的云端备份。