11.2 备份与升级


11.2 备份与升级

本节摘要:本节给「最坏的一天」准备剧本。备份部分的三要素:频率(全量与增量怎么配)、保留(留几份、留多久、存哪里)、恢复演练——核心立场是「没做过恢复演练的备份等于没有备份」,并给出演练的固定步骤与记录表。升级部分遵循第 2 章架构决定的固定次序:先跑 migration 进程完成数据库结构迁移,确认成功后再滚动重启其余服务;回滚方案要在升级前就位,而且和数据备份绑在一起——升级前的备份就是回滚的锚点。PG 18 与 Redis 8 的版本约束(官方技术栈口径)在升级检查单里占一行:组件版本与主程序版本的兼容矩阵要同时核对。

学习目标

  • 制定备份三要素:频率、保留、恢复演练。
  • 完成一次真正的恢复演练并留下记录。
  • 按固定次序执行升级:先 migration 再起服务。
  • 在升级前布好回滚锚点,并核对待办清单。

一、备份三要素

要素 决策内容 示例方案(示意)
频率 全量与增量的组合 每日全量 + 每小时增量(交易时段)
保留 留几份、留多久、放哪 7 天滚动 + 4 周末快照,异地/异机存放
恢复演练 定期真恢复到隔离环境 每月一次,计时并记录差异

要备份的东西按第 2 章架构盘点:PostgreSQL 里的策略配置、订单与成交记录、账户状态;策略代码与参数文件(含第 5 章 params 的版本历史);配置文件与环境清单。Redis 里的队列与缓存可以重建,明确「不备份、靠重建」反而减少恢复时的混乱。

# bash —— PostgreSQL 备份与校验(写法示意,凭据与路径按部署实际调整) # 每日全量(cron 调度),输出带日期与校验和 pg_dump -Fc -d quantdinger -f /backup/qd_$(date +%F).dump sha256sum /backup/qd_$(date +%F).dump > /backup/qd_$(date +%F).sha256 # 备份三问自检:能列出内容吗?校验和过吗?异地有副本吗? pg_restore --list /backup/qd_$(date +%F).dump > /dev/null && echo "结构可读" sha256sum -c /backup/qd_$(date +%F).sha256 ​

备份的完整性还可以再配一段自检脚本,把「三问」变成每日机器检查(纯标准库,路径与命名按部署实际调整):

# backup_verify.py —— 备份完整性自检(纯标准库,路径与命名按部署实际调整) import hashlib import os from datetime import date BACKUP_DIR = "/backup" # 示意路径 MAX_AGE_DAYS = 2 # 最新备份距今超过该天数即黄灯(示意值) def sha256(path): h = hashlib.sha256() with open(path, "rb") as f: for chunk in iter(lambda: f.read(1 << 20), b""): h.update(chunk) return h.hexdigest() def verify(): dumps = sorted(f for f in os.listdir(BACKUP_DIR) if f.endswith(".dump")) if not dumps: return "红:备份目录为空" newest = dumps[-1] stem = newest[:-len(".dump")] sum_path = os.path.join(BACKUP_DIR, stem + ".sha256") if not os.path.exists(sum_path): return f"红:{newest} 缺校验和文件" with open(sum_path, encoding="utf-8") as f: recorded = f.read().split()[0] if recorded != sha256(os.path.join(BACKUP_DIR, newest)): return f"红:{newest} 校验和不匹配" age = (date.today() - date.fromisoformat(stem[-10:])).days if age > MAX_AGE_DAYS: return f"黄:最新备份已 {age} 天" return f"绿:{newest} 完整且新鲜" ​

这段脚本与 bash 三问的分工:bash 那段在备份完成的当下跑,这段在每天固定时间独立跑——「备份那一刻是好的」与「现在仍然有一份好的」是两个命题,后者才会暴露「备份任务悄悄停了三天」这类慢性故障。它的输出可以直接当作 11.1 红绿灯之外的第 4 格。

二、恢复演练:备份的考试

恢复演练的固定步骤:在隔离环境起一套空实例;用最近一次备份恢复;跑三条验证——最新成交记录数与生产对得上、第 5.3 节的对账逻辑能通过、策略 on_start 预热后指标出值正常;全程计时并记录卡点。下表是演练记录的最小格式,每次演练填一行:

日期 备份点 恢复耗时 验证三项 发现的问题
(示例)第 1 次 当日全量 18 分钟 通过/通过/通过 环境变量未入备份清单,补

演练发现的典型问题与修复:恢复慢(提示备份粒度太粗)、缺文件(把配置纳入备份范围)、没人会做(把本节步骤写进值班手册)。演练的价值恰恰在于把这些问题暴露在事故之前。

三、升级:先 migration,再起服务

第 2 章的进程分工决定了升级次序:数据库结构变更由 migration 进程独立完成,其余服务重启后才按新结构工作。固定次序四步:

升级四步(次序固定,不可颠倒) 1. 预检:读版本发布说明;核对组件兼容矩阵(PG 18 / Redis 8,官方技术栈口径) 与当前部署版本;确认回滚锚点(本次升级前的备份)已就位 2. 冻结与备份:暂停开新仓(联动第 10 章应急开关)、等在途订单结算、做升级前备份 3. 迁移:先只启动 migration 进程 → 观察迁移日志与退出码 → 成功后才继续 4. 滚动重启:backend → 数据/scheduler/celery → trading-worker 最后恢复交易 → 跑 11.1 的三类关键指标红绿灯 → 解除冻结 ​

第 3 步是唯一的硬闸门:migration 失败就停在原地,不要带着「也许没影响」的侥幸去重启服务——新旧代码对表结构的假设不一致,是最常见的升级事故来源。

预检环节展开成可打勾的子清单(升级会前过一遍):

序 预检项 通过标准
1 发布说明通读 破坏性变更条目全部定位到自己的部署
2 组件兼容矩阵 PG 18 / Redis 8 与目标版本逐一核对(官方技术栈口径)
3 回滚锚点 升级前备份完成且通过 backup_verify 自检
4 冻结通知 停新单联动确认,相关方知晓升级窗口
5 migration 预估 迁移脚本已审读,耗时与锁表风险心里有数

第 5 项的「审读」不是走过场:迁移脚本里新增索引、改列类型这类操作的耗时与锁行为,决定了冻结窗口要留多长——低估了它,冻结期内交易恢复时间就会跟着漂移,第 10 章的开关解除时点也就不可信了。

四、回滚:升级前就布好的锚点

回滚方案的三件事:锚点(升级前备份 + 升级前版本号记入值班手册)、触发条件(升级后三类关键指标任一 critical,或对账不一致,10 分钟内不解决即回滚)、动作(停服、恢复备份、回退代码版本、按 11.2 一次完整重启)。回滚后要写事件记录:升级为什么失败、migration 是否需要手工清理——这是下次升级预检的输入。回滚剧本每半年至少演练一次,与恢复演练同场做。

五、常见问题与排查

问题 排查方向 要点
恢复演练卡在权限 隔离环境的角色与凭据未备 把凭据清单纳入演练物料
migration 跑到一半失败 迁移日志与退出码 停在原地评估,不带侥幸重启服务
备份校验耗时过长 校验时段安排 放低峰跑,别与备份任务抢 IO
回滚后发现丢了一段成交 锚点时间点之后仍有交易 冻结纪律就是为此:升级窗口内不留交易
Redis 数据要不要备份 可重建性判断 明确「不备份、靠重建」,写进手册

第四行是最疼的一类问题,根因几乎总是冻结不彻底:升级开始前停了新单,但已在途的订单回执落在备份点之后,回滚把它们带走了。这也解释了升级四步里「等在途订单结算」为什么是显式的一步而不是附带动作——回滚锚点的时间点,就是它必须罩住的时间点。

本节要点回顾

  • 备份三要素:频率、保留、恢复演练;没演练过的备份等于没有备份。
  • 升级四步:预检→冻结备份→先 migration→滚动重启;migration 是硬闸门。
  • 回滚锚点在升级前布好,触发条件与动作事先写死。
  • PG 18/Redis 8 的组件兼容矩阵在预检里核对(官方技术栈口径)。

单机自营到此已经完整。如果你的目标不止自用——把平台开放给多个用户或小团队——下一节看多租户 SaaS 能力要补哪些课。


作者与出处
原作者: 灏天文库
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U