备份做了不等于能恢复:采集数据恢复演练的工程实践

我们的采集数据备份跑了整整一年,每天自动执行,日志一片绿。直到有一天 SQLite 文件损坏,轮到「恢复」上场——结果恢复流程在文档里只有一句话「复制回去就行」,真执行时各种报错,折腾了大半天。那一刻我才明白:没演练过的备份,等于没有备份。 这篇记录我们后来补上的恢复演练机制。

第一步:先盘点备份现状

演练之前,先回答四个问题:

  1. 备了什么:数据库文件、原始响应归档、日志,是否都在备份范围内?
  2. 多久一次、保留多久:日备份?周归档?保留期覆盖「重放地平线」吗?
  3. 备份方式:SQLite 用在线 backup()(比直接复制文件安全);PostgreSQL 用 pg_dump 还是 pg_basebackup?
  4. 备份文件是否被验证过:能打开吗?行数对吗?

这轮盘点通常就有惊喜——比如发现某个「备份」脚本其实一直输出到错误路径。

第二步:定演练周期与范围

  • 每月快速演练:把最近一份备份恢复到临时库,校验行数和最新日期,确认「备份本身健康」
  • 每季度完整演练:恢复 + 跑一轮完整的日报流程(采集→入库→报表),对比产出的报表和当前环境的差异
  • 演练要测出 RTO/RPO:恢复花了多久(RTO)、丢失了多少数据(RPO)——别把没测过的数字写进承诺里

第三步:写演练脚本(恢复 + 校验)

import sqlite3

BACKUP = "backup/serp-2026-09-01.db"

def verify_restore():
    # 恢复:把备份灌进一个临时库(不动任何生产数据)
    restored = sqlite3.connect("restore_check.db")
    src = sqlite3.connect(BACKUP)
    src.backup(restored)          # SQLite 官方在线备份方式
    src.close()

    # 校验 1:能打开 + 基本查询正常
    row = restored.execute("SELECT COUNT(*) FROM results").fetchone()
    count = row[0]

    # 校验 2:数据新鲜度(备份里最新的采集日)
    latest = restored.execute("SELECT MAX(day) FROM results").fetchone()[0]

    # 校验 3:抽一条业务查询,比对生产库的近期结果
    sample = restored.execute(
        "SELECT keyword, day, MIN(rank) FROM results "
        "WHERE day >= ? GROUP BY keyword LIMIT 5",
        (latest,),
    ).fetchall()

    print(f"行数: {count} | 最新数据日: {latest}")
    for row in sample:
        print("  ", row)

    restored.close()

verify_restore()

要点:在临时库里恢复(别直接覆盖生产),校验要落在数据层面(行数、最新日期、业务查询),而不是「能打开」就完事。

PostgreSQL 同理:pg_restore 到一个临时库(或 Docker 起的临时实例),然后跑同样的行数与抽样校验。

第四步:恢复后的「业务验证」

光校验数字还不够,还要证明「业务能接着跑」:

  • 用恢复出来的库跑一遍日报生成的完整流程
  • 对比产出的报表和当前环境同一天的数字(差异即信号)
  • 跑一次采集写入,确认恢复后的库「能写、不报错、幂等正常」

备份健康 ≠ 业务可用——这一步补上的是最后一环。

第五步:记录演练结果

每次演练记录:日期、恢复耗时(RTO)、校验数字、发现的问题。文档化有两个作用:一是下次演练有对比基线;二是审计时「我们证明过备份可用」是有据可查的,不是口头保证。

踩坑记录

坑 1:备份文件是坏的但没人发现。 备份脚本静默失败、文件 0 字节、或者只备份了半截——不校验的备份等于没备份。每次备份后自动跑一次校验(能打开 + 行数 > 0)。

坑 2:恢复流程只在生产环境验证过。 换个环境(新服务器、容器)恢复就失败:版本差异、路径权限、依赖缺失。演练就为了暴露这些——尽量在「全新环境」里演练。

坑 3:只验证「能打开」。 SQLite 能打开但缺了最近的表、Postgres 恢复一半报错却没人看退出码。校验要落数据层面。

坑 4:演练直接覆盖生产数据。 一个手滑,演练变成事故。永远在临时库/临时实例上演,演练脚本里禁止连生产路径。

坑 5:把没测过的 RTO/RPO 写进文档。 「30 分钟内恢复」如果没有演练数据支撑,出事时就是违约。演练记录就是这两个数字的唯一来源。

坑 6:演练结果不存档。 三个月后问「上次演练怎么样」答不上来。记录进文档,最好也进审计。

工程清单

  1. 盘点备份范围、频率、方式、是否验证过
  2. 月快速 + 季完整的演练节奏,测出 RTO/RPO
  3. 演练脚本:临时库恢复 + 行数/日期/业务查询三层校验
  4. 恢复后跑一遍完整业务流程(日报 + 写入)
  5. 演练结果存档,作为审计依据
  6. 备份任务本身带校验,静默失败要告警

恢复演练的本质,是把「我们相信备份有效」换成「我们验证过备份有效」。前者是自我安慰,后者是工程事实。采集数据的备份尤其值得演练——数据是时间序列,丢一天的窗口永远补不回来,而「恢复不了」比「丢数据」更糟:前者是灾难,后者至少还有备份。

校验和业务查询里用到的字段(day、rank)语义见 SerpBase 官方文档。你们的备份演练过恢复吗?评论区聊聊第一次演练翻车的经历。

posted @ 2026-09-29 09:40  蜘蛛人  阅读(10)  评论(0)    收藏  举报