跳转至

备份与恢复窗口

一个目标的历史是一条链:按计划进行的全量备份,以及两次全量之间连续不断的变更流(Postgres WAL、MySQL binlog)。 凡是被某个全量备份及其后续变更覆盖的每一秒,都可以恢复。这段跨度就是时间点窗口。

gantt
  dateFormat  YYYY-MM-DD HH:mm
  axisFormat  %a %d
  section Full backups
  daily full      :milestone, 2026-10-01 02:07, 0d
  daily full      :milestone, 2026-10-02 02:07, 0d
  daily full      :milestone, 2026-10-03 02:07, 0d
  daily full      :milestone, 2026-10-04 02:07, 0d
  section Change stream
  WAL / binlog segments :active, 2026-10-01 02:07, 2026-10-04 18:00

全量备份

引擎 模式 工具 恢复
Postgres 15–17 physical(默认) 通过复制协议执行 pg_basebackup -Ft -X fetch 基础备份 + WAL 重放,精确到秒
Postgres 15–17 logical pg_dump -Fc,每个数据库一个对象;支持排除表和选择性导出 pg_restore,存在物理链时再通过它重放 WAL
MySQL 8.4 logical mysqldump --single-transaction --source-data=2(例程、事件、触发器) 加载 + binlog 重放,精确到秒

每个备份都会记录一份清单:数据库、表、列、索引、行数估计和大小,以及存在迁移表时的迁移版本。 控制台和 tenax diff 比较的就是这些内容。

变更流

每个启用了时间点恢复的目标都有一个流式传输程序:

  • Postgres:Tenax 自己的 WAL 接收器,基于物理复制槽(ADR 0005)。 复制槽保证流式传输程序重启期间不丢失任何 WAL。
  • MySQL:从上一次全量备份的 binlog 位置开始执行 mysqlbinlog --read-from-remote-server --raw --stop-never。

每个段写完即上传,仍在写入的段每隔 partialInterval(默认 60 秒)上传一次,所以最后一分钟同样可以恢复。 每个段都附带一份摘要:每张表的提交、插入、更新和删除次数、DDL,以及可选的脱敏行预览。

如果流中断(复制槽丢失、binlog 被清除),Tenax 会把这条链标记为断裂,发出告警(TenaxStreamChainBroken), 启动一条新链并立即进行一次全量备份(原因为 chain-restart)。旧窗口在断点之前的部分仍然可以恢复。

保留

全量备份按层级保留;变更段只保留到窗口所需的时间范围;其余一切由垃圾回收清除,但已固定的备份绝不会被删除。

预设 窗口 每日 每周 每月 每年
prod 7 天 14 8 12 5
dev 3 天 7 4 3 0
critical 14 天 30 12 24 7

一天中第一个提交的全量备份就是当天的每日备份;一个 ISO 周中的第一个就是当周的每周备份,依此类推。 一个备份可以同时属于多个层级,不会产生任何复制。固定(tenax pin <id> --reason …)可以让备份不受层级约束地保留下来。 GC 在删除前会先把计划写入存储桶,先删 manifest 再删数据,并在一天后清理孤立的和未完成的上传。

存储格式

<store prefix>/<org>/<project>/<namespace>/<target>/
  full/<backup-id>/manifest.json            # written last: the commit marker
  full/<backup-id>/data/<database>.zst.age  # dumps or the base backup tar
  full/<backup-id>/inventory.json.zst.age   # also readable with the catalog key
  full/<backup-id>/tags.json                # tiers, pins, verification results
  stream/…/<segment>.zst.age                # WAL / binlog segments and their summaries
  stream/position.json                      # the streamer's confirmed position
  • 压缩:按策略设定的级别执行 zstd 压缩,只压缩一次,在加密之前进行;默认开启 (ADR 0013)。compression.algorithm: none 则按原样存储数据。
  • 加密:每个对象都在客户端用 age 加密给存储的接收者。存储桶永远看不到明文。
  • 完整性:每个对象的大小和 SHA-256 都记录在 manifest 中,每次读取时都会校验。
  • 提交:备份只有在写入 manifest.json 之后才算存在。在此之前发生崩溃只会留下由 GC 清除的孤立对象, 绝不会出现一个看起来完整的备份。

负载控制

备份从不盲目地与生产业务争抢资源:有按主机、按目标和全局的并发槽位、带宽限制、优先级、计划时间抖动, 以及一个负载保护机制,在服务器活跃连接过多、复制延迟过大或存在长时间运行的事务时推迟全量备份。 所有这些都在 BackupPolicy 中设置。