2705 字
约 9 分钟
13
S15-X13 · 备份与恢复:策略、演练与校验

"我们有备份"这句话在真正需要恢复的那一刻才有含义。判断一个备份体系是否成立,不看备份任务的成功率,而看最近一次从备份恢复出一个可用实例是什么时候、花了多久、丢了多少数据。这篇按策略、方式、校验、演练的顺序拆。


1. 三要素:RPO、RTO 与保留期

要素 回答的问题 决定的技术手段 成本方向
RPO 最多能丢多少数据 备份频率 + binlog 持续归档 越小越贵
RTO 多久必须恢复可用 备份形态(物理/逻辑)、是否预热备用实例 越小越贵
保留期 要留多久 存储介质与归档层 越长越贵

三者互相拉扯:RPO 做到 1 分钟,意味着必须有持续增量归档;RTO 做到 10 分钟,意味着增量恢复链条要短、或者干脆有热备。把三个数字先写下来,再选工具——反过来"先选工具再定指标"通常得到一套跑得动但恢复不了的流程。

一个可以照抄的表述方式:「RPO 5 分钟,RTO 30 分钟,全量保留 7 天、归档保留 90 天」。指标不写清楚,演练时也无法判断通过与否。


2. 备份类型与恢复链条

类型 每次的输入 恢复需要 空间与时间
全量 全部数据 单个文件即可 空间最大,恢复最简单
增量 上次备份以来的变化 全量 + 全部中间增量 空间最小,链条最长
差异 上次全量以来的变化 全量 + 最近一次差异 折中

恢复链条的长度直接等于风险:增量策略里只要中间有一份损坏或缺失,链条就断在那里,后面的增量全部用不了。所以增量备份必须配"定期全量 + 链条完整性校验",并且要在备份清单里写清每一份增量的父节点是谁。


3. MySQL:三种方式对照

方式 一致性保证 恢复粒度 适用
mysqldump 需 --single-transaction(InnoDB) 库/表 中小库、跨版本迁移
xtrabackup 物理一致,支持增量 实例 大库、RTO 要求低
binlog 归档 逐事务 时间点 配合全量做 PITR

逻辑备份的一致性关键在 --single-transaction:它在可重复读隔离级别下开一个事务拿到一致快照,避免备份期间写入导致的不一致。但这条命令只对 InnoDB 有效,MyISAM 表仍会锁表或产生不一致。

$ mysqldump --single-transaction --quick --routines --triggers --events \
    --set-gtid-purged=OFF --default-character-set=utf8mb4 \
    -u backup -p dbname > /backup/db_$(date +%F_%H%M).sql

--routines、--triggers、--events 三个必须显式加,否则存储过程、触发器、定时事件全部丢失——这类丢失在恢复后往往几周才被发现。

物理备份:

$ xtrabackup --backup --target-dir=/backup/xtra/$(date +%F) \
    --user=backup --password=*** 
$ xtrabackup --prepare --target-dir=/backup/xtra/$(date +%F)

--prepare 是必须的一步,它回放未提交事务、让数据文件达到一致状态;只 --backup 不 --prepare 的目录不能直接启动。

PITR 的完整流程是四步:先恢复最近一次全量,再重放从全量结束到目标时刻之间的 binlog,最后 RESET MASTER/PURGE 并重新挂接复制。缺一步都会得到一个"看起来能启动但数据停在更早时刻"的实例。

$ mysqlbinlog --stop-datetime='2026-11-09 03:00:00' \
    /backup/binlog/bin.000123 /backup/binlog/bin.000124 | mysql -u root -p

--stop-datetime 与 --start-datetime 的时区基准是 binlog 里记录的时间,而 binlog 时间取的是服务器时区。备份文件的时间戳、binlog 的时间戳、你笔记本上的时间,三者不一致是 PITR 失败的常见原因。


4. 文件备份:不能热拷

数据库文件在运行中被修改,直接 cp 得到的是一堆互相不一致的页。可行的手段只有三种:停服务拷、文件系统快照、或让数据库自己产出备份。

快照的原子性取决于卷管理器:LVM 快照、云盘快照通常是瞬时一致点;跨多个卷的"逐个打快照"则不是原子操作,中间有时间差。要让多个卷一致,得先 fsfreeze 再打快照再解冻。

$ fsfreeze -f /data && lvcreate -L 20G -s -n snap_data /dev/vg0/data && fsfreeze -u /data

fsfreeze 期间写入会被挂起,时间窗口必须短;忘了 -u 会让挂载点一直冻结,表现为所有写操作卡住——这类"备份把业务弄挂"的事故并不少见。


5. 存放:异地、介质、加密

维度 最低要求 常见失误
位置 与源不同机器、不同可用区 备份和数据库在同一块盘上
介质 与生产盘不同物理设备 同一块云盘挂载成两个目录
加密 传输 TLS + 静态加密 明文 tar 传到对象存储公共桶
权限 备份账号只读 + 目录 700 备份目录 world-readable
版本 每份带版本与校验和 只有文件名,无法判断内容

"备份在同一块盘上"是最常见的假备份:盘坏了两样一起没。异地这一条不是合规要求,是故障域的常识。


6. 校验的四层

层 做什么 能发现什么 频率
1 完整性 文件大小、校验和、gzip -t 传输截断、磁盘坏块 每次备份后
2 可恢复性 恢复到临时实例并启动 权限、版本、参数不匹配 每周
3 内容抽样 行数、关键表、最大 ID 对比 逻辑丢失、字符集损坏 每次恢复演练
4 演练记录 记录耗时、步骤、偏差 流程本身的缺陷 每月

前两层自动化,第三层半自动,第四层必须人做。只做第一层等于只验了文件没坏,而"文件没坏但恢复不起来"是更常见的失败模式。

$ sha256sum /backup/db_*.sql > /backup/SHA256SUMS
$ gzip -t /backup/db_2026-11-09.sql.gz && echo ok
$ mysql -e 'SELECT COUNT(*) FROM db.orders' > /tmp/after.txt
$ diff <(sort /tmp/before.txt) <(sort /tmp/after.txt)

7. 恢复演练:步骤清单

# 1) 隔离环境:不同端口、不同目录、不同实例名
$ mysqld --initialize-insecure --datadir=/restore/data
$ mysqld --datadir=/restore/data --port=3307 --socket=/restore/mysql.sock &
# 2) 导入
$ mysql --socket=/restore/mysql.sock -u root < /backup/db_2026-11-09.sql
# 3) 对齐 binlog 到目标时刻
$ mysqlbinlog --stop-datetime='...' bin.000123 | mysql --socket=/restore/mysql.sock -u root
# 4) 抽样比对
$ mysql --socket=/restore/mysql.sock -e 'CHECKSUM TABLE db.orders'

清单里必须固定三样记录:开始时间、可查询时间、业务可用时间。第一个数字说明备份本身多大,第二个说明恢复脚本有效,第三个才是 RTO 的真实值——通常比第二个大得多,因为还要切流量、改配置、验证接口。

演练要定期换人执行。写脚本的人做演练会无意识地跳过"他不知道但别人会踩"的步骤。


8. 恢复后的收尾

项 检查 不处理的后果
账号与权限 应用账号是否存在、密码是否正确 应用连不上,或权限过大
AUTO_INCREMENT 各表当前值 新写入撞主键,报重复键
字符集与排序 库/表/连接三层一致 中文乱码、排序错乱
复制 CHANGE REPLICATION SOURCE TO 重建 从库停在旧位点
定时事件 event_scheduler 是否开启 定时任务静默不跑
外键与约束 SET FOREIGN_KEY_CHECKS 是否被遗留为 0 数据完整性失效

AUTO_INCREMENT 是恢复后最容易漏的一项:逻辑备份不带自增计数器时,恢复后计数器从最大值重新算,如果有旧数据被删过,新插入就可能撞已存在的 ID。


9. 排错

现象 原因 处理
备份任务一直成功,恢复时导入失败 版本不一致或 SQL 模式差异 记录源/目标版本,恢复前 SET sql_mode 对齐
恢复后发现存储过程全没了 mysqldump 未带 --routines 重备一次,并写进脚本模板
PITR 后数据停在更早时刻 binlog 时间基准与预期时区不一致 统一时区,用 --start-position 精确控制
增量恢复链断在中途 某份增量缺失或损坏 改为"全量 + 差异",并校验链条父节点
恢复把生产库覆盖了 演练与生产用了同一个实例/端口 强制隔离端口与 socket,加 --one-database 白名单
xtrabackup 恢复后启动失败 漏了 --prepare 补 prepare 再启动
备份盘写满导致备份截断 未做容量告警与轮转 加容量阈值告警,保留策略落地
备份文件读不出 加密密钥丢失或权限错误 密钥与备份分开保管但可追溯

10. 边界

场景 更合适的做法 理由
只防误删单表 延迟复制从库 / 表级闪回 全量恢复代价过大
跨机房的强一致 同步复制或共识协议 备份不等于高可用
需要秒级 RPO binlog 实时投递 + 对象存储 定时任务做不到
归档合规 对象存储归档层 + 保留锁 本地磁盘无法证明不可篡改
巨型库 物理备份 + 增量 + 并行流 逻辑导出时间不可接受

11. 自测

  1. RPO 与 RTO 分别约束哪一环?为什么缩短 RTO 往往要先缩短恢复链条?
  2. mysqldump --single-transaction 对 MyISAM 表为什么无效?
  3. xtrabackup 只做 --backup 不做 --prepare,恢复后会怎样?
  4. PITR 时数据停在比预期更早的时刻,最先核对什么基准?
  5. 为什么说"备份和生产在同一块盘上"等于没有备份?
  6. 恢复后新插入数据撞主键,通常是哪一项没处理?
S15-X13 · 备份与恢复:策略、演练与校验
https://www.clxhxhhr.top/posts/5870/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。