备份脚本每天在跑,日志显示成功,监控面板上一片绿色。直到某天需要真正用它恢复数据的时候,才发现文件是坏的、binlog 有断档、或者恢复出来少了几个小时的记录。备份本身不产生价值,能被成功用于恢复的备份才产生价值。
先把两个指标定义清楚
RPO(Recovery Point Objective):发生故障时,允许丢失多少时间窗口内的数据。回答的是”能丢多少”。
RTO(Recovery Time Objective):从故障发生到服务恢复可用,允许用多久。回答的是”要多久”。
这两个数字必须由业务方给出,而不是技术团队拍。定 RPO 时问:”最近两小时的订单全部丢失,业务影响是什么,人工补录的成本是多少?”定 RTO 时问:”服务中断三十分钟和一小时的差别,对业务意味着什么?”
技术团队的任务是回答”在这个 RPO/RTO 下需要什么方案、成本多少”。对应关系大致如下:
| 目标 | 需要的手段 |
|---|---|
| RPO 数十分钟,RTO 数小时 | 每天一次全量备份即可 |
| RPO 数分钟,RTO 数十分钟 | 全量 + 增量 + binlog 归档(PITR) |
| RPO 秒级 | 半同步复制到备机,或同步提交的多副本 |
| RTO 分钟级 | 热备实例常驻运行,随时可切换 |
把 RPO 写成 0 是不诚实的。即使是同步复制,主库在写入返回前挂掉,客户端收到的是超时而非成功,业务上该事务是否成立本身就存疑。
物理备份与逻辑备份
逻辑备份导出的是 SQL 语句或数据行。典型工具是 mysqldump:
mysqldump --single-transaction --master-data=2 --routines --triggers
--databases app_db > app_db_$(date +%F).sql
--single-transaction 让导出在一个一致性快照里完成,不锁表;--master-data=2 会把导出时刻的 binlog 位点和文件名写成注释记录下来,这对后续 PITR 是必要信息。
逻辑备份的特点:
- 可读、可编辑,可以只恢复一张表或一部分数据。
- 跨版本、跨架构迁移友好,适合数据搬迁和小规模的数据订正导出。
- 恢复速度慢。恢复时要重新解析 SQL、重建索引、重做约束检查,通常是 CPU 密集的。几百 GB 的库恢复时间可能以小时计。
- 导出过程本身对源库产生读压力,长事务场景下还可能堆积大量 undo。
物理备份复制的是数据文件本身。典型工具是 Percona XtraBackup 或 MySQL Enterprise Backup:
xtrabackup --backup --target-dir=/backup/full/2026-10-21
--user=backup --password=xxx --parallel=4
# 准备阶段:应用 redo log,让文件集达到一致性状态
xtrabackup --prepare --target-dir=/backup/full/2026-10-21
物理备份的特点:
- 恢复快,本质是文件拷贝加一次启动时的崩溃恢复,速度接近磁盘 IO 上限。
- 备份过程也快,通常比逻辑导出快一个量级。
- 不能跨大版本、不能跨存储引擎配置,机器上的 MySQL 版本与配置必须兼容。
- 粒度是整个实例(或整个库),恢复单张表需要额外技巧。
- 备份文件体积等于数据量,压缩率低时存储成本高。
常见做法是物理备份承担常规全量与增量以保证 RTO,逻辑备份周期性补一次,用于单表/单行级恢复与跨版本迁移。
全量加增量与 PITR
每天一次全量、每小时一次增量的组合比较常见。增量备份依赖 LSN(Log Sequence Number),只拷上次备份以来变化过的页面:
# 周日全量
xtrabackup --backup --target-dir=/backup/full/sun
# 周一增量,基于全量
xtrabackup --backup --target-dir=/backup/inc/mon
--incremental-basedir=/backup/full/sun
# 周二增量,基于周一的增量
xtrabackup --backup --target-dir=/backup/inc/tue
--incremental-basedir=/backup/inc/mon
恢复时需要按顺序逐个 prepare:
xtrabackup --prepare --apply-log-only --target-dir=/backup/full/sun
xtrabackup --prepare --apply-log-only --target-dir=/backup/full/sun
--incremental-dir=/backup/inc/mon
xtrabackup --prepare --apply-log-only --target-dir=/backup/full/sun
--incremental-dir=/backup/inc/tue
xtrabackup --prepare --target-dir=/backup/full/sun # 最后一步不加 --apply-log-only
每一步都不可逆,中间某一步失败会污染整个 backup 目录。增量链越长单点失败风险越大,这也解释了为什么增量链通常不超过一周。
增量备份解决的是”恢复到备份时刻”,要把 RPO 压到分钟级,还需要 binlog 的连续归档(PITR,Point-in-Time Recovery)。做法是持续把主库产生的 binlog 实时复制到备份存储:
# 常驻进程,持续拉取 binlog 到本地目录
mysqlbinlog --read-from-remote-server --host=master --user=repl
--raw --stop-never
--result-file=/backup/binlog/ master-bin.000001 &
恢复流程变成:
# 1. 恢复到最近一次全量+增量的时间点
xtrabackup --copy-back --target-dir=/backup/full/sun
# 2. 找到全量备份记录的位点(备份目录下的 xtrabackup_binlog_info)
cat /backup/full/sun/xtrabackup_binlog_info
# master-bin.000042 8934521
# 3. 从这个位点开始重放 binlog,直到故障前一刻
mysqlbinlog --start-position=8934521
/backup/binlog/master-bin.000042
/backup/binlog/master-bin.000043
| mysql -u root -p
如果要恢复到某个精确时刻:
mysqlbinlog --start-position=8934521 --stop-datetime="2026-10-21 14:23:00"
/backup/binlog/master-bin.000042 | mysql -u root -p
整条链路的可靠性取决于最弱一环:binlog 归档进程因磁盘满静默退出几天后,PITR 就只剩”恢复到几天前”的能力,而备份监控面板可能依然是绿的。归档去向需要单独监控:进程存活、最新 binlog 的时间戳与当前时间的差距、存储剩余空间。
校验:验证”能恢复”而不是”备份成功”
备份任务返回成功只说明拷贝动作没报错。合理的校验分三层:
第一层:文件完整性
# 备份时记录校验和
find /backup/full/sun -type f -exec md5sum {} ; > /backup/full/sun.md5
# 恢复前校验
md5sum -c /backup/full/sun.md5
参考 XtraBackup 输出的位点信息:
tail -5 /backup/full/sun/xtrabackup_checkpoints
# backup_type = full-backuped
# from_lsn = 0
# to_lsn = 8934521
# last_lsn = 8934530
# compact = 0
第二层:可启动。把备份恢复到一个隔离的实例上,能正常启动、能通过基本的连通性检查。这一步能发现绝大多数问题。
第三层:数据一致性。抽样核对行数与关键字段的 checksum:
-- 在恢复出来的实例上执行
SELECT COUNT(*) AS rows_cnt,
BIT_XOR(CRC32(CONCAT_WS('|', id, amount, status))) AS ck
FROM orders WHERE created_at >= '2026-10-01';
与生产环境同口径比对。把这个值定期记下来形成时间序列,不必每次全量比对就能发现异常。
恢复演练
演练是把所有假设验证一遍的唯一方式。可执行的演练流程:
- 确定目标:要验证的具体场景(整机故障 / 误删表 / 机房故障),以及期望的 RTO。
- 准备隔离环境:独立的网络与存储,与生产没有任何连通路径。
- 按 runbook 执行:严格照文档一步步执行,不做即兴调整。文档不准确的地方要当场记录。
- 计时:从”决定恢复”到”业务校验通过”,每阶段单独计时——拉备份、解压、prepare、启动、追 binlog、业务校验。
- 验证:数据行数、关键 checksum、抽样业务查询、应用连通性。
- 复盘:更新 runbook,修正计时假设,把发现的问题转成待办项。
频率取决于业务变更速度。日常全量备份、月度演练是常见基线。每次重大变更(大版本升级、表结构大调整、迁移)之后应该追加一次。
恢复环节最容易低估的是传输时间。备份通常在对象存储上,几 TB 数据从远端拉回本地的时间可能远超 prepare 时间。这一项必须在 RTO 预算里单独列出,而不是笼统计入”恢复时间”。
恢复环境与生产的差异风险
- 机器规格不同。演练用低配机器,真实故障可能用高配或反之。CPU 核数影响 InnoDB 并发崩溃恢复速度,磁盘 IOPS 影响 copy-back 速度,两者都实质影响 RTO。
- 版本与参数不一致。恢复到不同版本可能失败或行为不同。
innodb_log_file_size、innodb_page_size不匹配会直接导致启动失败。 - 磁盘布局不同。备份记录的是文件路径,恢复时目录结构要与
my.cnf匹配。 - 网络与依赖。恢复实例依赖的配置中心、DNS、监控代理若不能连通,启动后可能无法进入可用状态。
- 权限与凭据。备份包含用户表,但应用连接用的是另一套凭据,恢复实例上的账号需要重新确认。
常见的失败模式
备份文件损坏但不是全部损坏。只损坏了一页数据的情况下,实例可能能启动,某些查询才会失败。这类问题靠 dump 时的异常是不够的,需要靠 checksum 比对发现。
binlog 断档。归档进程在某个时刻中断,中间缺了几个文件。追 binlog 时会在文件中点出错。防范手段是监控 mysqlbinlog --stop-never 进程的存活以及生成文件的连续性(文件名序号不能有缺口)。
# 检查 binlog 归档是否连续
ls /backup/binlog/ | awk -F. '{print $2}' | awk 'NR>1 && $1!=prev+1 {print "GAP at", $1} {prev=$1}'
恢复后数据不一致但没有报错。这种情况最难发现,通常出现在:备份期间没有正确使用一致性快照(漏了 --single-transaction),导致导出的数据跨表不一致;或者 binlog 重放时因为 server_id 相同跳过了某些事件;或者 GTID 模式下重复执行了已执行的 GTID。
误删后才发现 RPO 不满足。最常见的场景是:DBA 发现误删数据时已经是两小时后,而上线前的备份策略只有每日全量,丢失了一天的数据。误操作场景下 RPO 的实际需求是”秒级”,这与硬件故障场景完全不同。针对误操作的防护(延迟从库、sql_safe_updates、操作审批与双人复核)需要单独设计,不能指望备份。
备份目录权限开放。备份包含完整的数据内容,包括敏感信息。访问控制、加密存储、传输加密都应该与生产库的等级相同,演练环境用完之后的数据也要清理。
RPO 和 RTO 是业务承诺,备份只是实现这个承诺的手段之一。真正决定故障时能否兑现承诺的是演练记录里那个真实的计时数字,而不是备份任务每天的”成功”日志。
评论(0)
还没有评论,来抢沙发。
请 登录 后参与评论。