3、物理备份实战:XtraBackup全量备份原理、增量备份与差异备份的区别、备份集验证
物理备份,说白了就是直接拷贝数据库的数据文件。逻辑备份用 mysqldump 导出 SQL,物理备份则直接操作磁盘上的 .ibd、.frm 这些文件。你想想看,几十 GB 的数据库,用 mysqldump 导出再导入,那得等到什么时候?物理备份的速度优势就在这里体现出来了。
XtraBackup 是目前 MySQL 物理备份的事实标准。我最早接触它是在 Percona 5.6 时代,那时候还叫 innobackupex,现在已经统一成 xtrabackup 命令了。嗯,这里要注意,从 8.0 版本开始,Percona 已经废弃了 innobackupex,全部用 xtrabackup 替代。
核心认知:XtraBackup 备份的是物理文件,不是逻辑数据。它直接拷贝 InnoDB 的数据文件、redo log、undo log,以及表结构定义文件。恢复时也是直接把这些文件放回数据目录,不需要逐条执行 SQL。
3.1 全量备份原理
全量备份,就是把整个数据库实例的所有数据文件完整拷贝一份。XtraBackup 是怎么做到在线热备而不影响业务的?
它的核心机制是 redo log 的持续跟踪。备份开始时,XtraBackup 会启动一个后台线程,专门监控 redo log 的变化。然后它开始拷贝数据文件,这个过程可能持续几分钟甚至几小时。在拷贝期间,数据库还在不断写入,数据文件也在变化。
这就产生了一个问题:拷贝出来的数据文件,在时间点上是不一致的。比如表 A 的数据拷贝完了,但表 B 还没拷贝完,这时候数据库崩溃了,恢复时就会出问题。
XtraBackup 的解决方案是:拷贝完所有数据文件后,利用之前跟踪的 redo log,对数据文件执行一次 崩溃恢复(apply redo log)。这样就能把数据文件推到同一个一致的时间点。
我个人习惯把全量备份的命令写成脚本,加上时间戳命名,方便后续管理:
# 全量备份命令示例
xtrabackup --backup \
--target-dir=/backup/full/$(date +%Y%m%d_%H%M%S) \
--datadir=/var/lib/mysql \
--user=backup_user \
--password=backup_pass \
--host=127.0.0.1 \
--port=3306
备份完成后,还需要执行一步 prepare 操作,让备份集处于可用状态:
# 对全量备份执行 prepare
xtrabackup --prepare \
--target-dir=/backup/full/20250315_020000
为什么要 prepare?因为备份出来的数据文件,redo log 可能还没完全应用,处于"不一致"状态。prepare 就是模拟 MySQL 的崩溃恢复过程,把数据文件推到一致点。我在项目中遇到过有人忘记执行这一步,直接拿备份文件去恢复,结果 MySQL 启动时报错,数据文件损坏。嗯,这个坑踩过一次就再也不会忘了。
3.2 增量备份与差异备份的区别
全量备份虽然完整,但每天做一次全量,磁盘空间和备份时间都扛不住。这时候就需要增量备份或差异备份来帮忙。
这两个概念经常被混淆,我简单解释一下:
- 增量备份:只备份自上一次备份(无论全量还是增量)以来发生变化的数据。
- 差异备份:只备份自上一次全量备份以来发生变化的数据。
举个例子你就明白了。假设周一做了全量备份,周二、周三、周四分别做备份:
| 备份策略 | 周二备份内容 | 周三备份内容 | 周四备份内容 |
|---|---|---|---|
| 增量备份 | 周一~周二变化 | 周二~周三变化 | 周三~周四变化 |
| 差异备份 | 周一~周二变化 | 周一~周三变化 | 周一~周四变化 |
看到了吗?差异备份每次都是跟全量比,所以越往后备份的数据量越大。增量备份只跟上一次比,所以每次数据量相对较小。
恢复时的区别更明显:
- 增量恢复:需要全量 + 所有增量备份链,按顺序 apply。比如周四出问题,需要恢复顺序是:全量 → 增量1 → 增量2 → 增量3。
- 差异恢复:只需要全量 + 最后一次差异备份。比如周四出问题,只需要:全量 → 差异3。
我曾经在一个日活千万的电商平台上做过对比测试。每天数据变更量约 5GB,全量备份 200GB。增量备份每天 5GB,差异备份从周一的 5GB 涨到周日的 35GB。恢复时间上,增量恢复需要 2 小时(因为要按顺序 apply 7 个增量),差异恢复只需要 40 分钟。
所以我的建议是:
- 如果数据变更量小,且恢复时间要求不高,用增量备份可以节省磁盘空间。
- 如果数据变更量大,或者对恢复速度有严格要求,用差异备份更合适。
- 我个人习惯是:周日做全量,周一到周六做差异备份。这样恢复时最多只需要全量 + 一个差异,速度快,管理也简单。
XtraBackup 的增量备份命令如下:
# 基于全量备份做增量备份
xtrabackup --backup \
--target-dir=/backup/inc1/ \
--incremental-basedir=/backup/full/20250315_020000 \
--datadir=/var/lib/mysql \
--user=backup_user \
--password=backup_pass
# 基于上一次增量做下一次增量
xtrabackup --backup \
--target-dir=/backup/inc2/ \
--incremental-basedir=/backup/inc1/ \
--datadir=/var/lib/mysql \
--user=backup_user \
--password=backup_pass
增量备份的 prepare 需要特殊处理,不能直接 --prepare:
# 先 prepare 全量备份(带 --apply-log-only 防止回滚未提交事务)
xtrabackup --prepare --apply-log-only \
--target-dir=/backup/full/20250315_020000
# 依次 apply 每个增量到全量上
xtrabackup --prepare --apply-log-only \
--target-dir=/backup/full/20250315_020000 \
--incremental-dir=/backup/inc1/
xtrabackup --prepare --apply-log-only \
--target-dir=/backup/full/20250315_020000 \
--incremental-dir=/backup/inc2/
# 最后一个增量不需要 --apply-log-only,让 XtraBackup 执行完整的崩溃恢复
xtrabackup --prepare \
--target-dir=/backup/full/20250315_020000 \
--incremental-dir=/backup/inc3/
注意:增量备份链不能断。如果中间某个增量备份损坏或丢失,后续所有增量都无法应用。我曾经在客户现场遇到过这种情况——磁盘故障导致一个增量备份文件损坏,结果只能恢复到上一个全量备份的时间点,丢失了将近 12 小时的数据。所以增量备份的完整性验证非常重要。
3.3 备份集验证
备份做完了,不代表就万事大吉了。你想想看,如果备份文件本身是坏的,等到需要恢复时才发现,那可就真叫天天不应了。
备份集验证,说白了就是确认备份文件是可用的、完整的。我把它分为三个层次:
3.3.1 基本完整性检查
XtraBackup 在备份完成后,会在备份目录下生成一些元数据文件,比如 xtrabackup_checkpoints、xtrabackup_info。我们可以通过这些文件快速判断备份是否完整。
# 查看备份的 checkpoint 信息
cat /backup/full/20250315_020000/xtrabackup_checkpoints
# 输出示例
backup_type = full-prepared
from_lsn = 0
to_lsn = 1234567890
last_lsn = 1234567895
compact = 0
recover_binlog_info = 0
关键字段说明:
backup_type:如果是full-prepared,说明已经执行过 prepare,可以直接用于恢复。to_lsn和last_lsn:这两个值应该非常接近,如果差距过大,说明备份过程中可能有问题。
3.3.2 模拟恢复验证
这是最可靠的验证方式。把备份集恢复到一台测试实例上,然后启动 MySQL,检查数据是否完整。
# 1. 准备一个空的恢复目录
mkdir -p /tmp/restore_test
# 2. 执行 restore(拷贝文件到恢复目录)
xtrabackup --copy-back \
--target-dir=/backup/full/20250315_020000 \
--datadir=/tmp/restore_test
# 3. 修改权限
chown -R mysql:mysql /tmp/restore_test
# 4. 启动 MySQL 测试实例
mysqld --datadir=/tmp/restore_test --port=3307 &
# 或者用 mysqld_safe
# 5. 连接测试实例,检查数据
mysql -h127.0.0.1 -P3307 -e "SELECT COUNT(*) FROM mydb.mytable;"
# 6. 验证完成后关闭测试实例
mysqladmin -h127.0.0.1 -P3307 shutdown
我在项目中遇到过这样的情况:备份文件看起来完整,checkpoints 文件也正常,但模拟恢复时发现某个表空间文件损坏,MySQL 启动报错。幸亏提前发现了,否则真到灾难恢复时,后果不堪设想。
我的经验:每周至少做一次完整的模拟恢复验证。不要只在备份脚本里加个 --check 参数就完事了。真正的恢复流程涉及文件权限、路径配置、参数设置等多个环节,只有完整跑一遍才能发现问题。
3.3.3 自动化验证脚本
手动验证太麻烦,我一般会写一个自动化脚本,每天凌晨备份完成后自动执行验证:
#!/bin/bash
# 备份验证脚本示例
BACKUP_DIR="/backup/full/$(date +%Y%m%d)"
TEST_DIR="/tmp/restore_verify"
MYSQL_DATA="/var/lib/mysql_test"
# 清理上次验证环境
rm -rf $TEST_DIR $MYSQL_DATA
mkdir -p $TEST_DIR $MYSQL_DATA
# 执行 prepare(如果还没 prepare)
xtrabackup --prepare --target-dir=$BACKUP_DIR
# 执行 restore
xtrabackup --copy-back --target-dir=$BACKUP_DIR --datadir=$MYSQL_DATA
chown -R mysql:mysql $MYSQL_DATA
# 启动测试实例
mysqld --datadir=$MYSQL_DATA --port=3307 \
--socket=/tmp/mysql_verify.sock \
--skip-networking &
sleep 5
# 检查关键表
mysql -S /tmp/mysql_verify.sock -e "
SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_ROWS
FROM information_schema.TABLES
WHERE TABLE_SCHEMA NOT IN ('mysql','sys','performance_schema','information_schema')
ORDER BY TABLE_ROWS DESC LIMIT 10;
"
# 检查是否有损坏的表
mysql -S /tmp/mysql_verify.sock -e "CHECK TABLE mydb.mytable;"
# 关闭测试实例
mysqladmin -S /tmp/mysql_verify.sock shutdown
# 清理
rm -rf $TEST_DIR $MYSQL_DATA
echo "备份验证完成: $(date)"
这个脚本可以放在 crontab 里每天执行。如果验证失败,就发送告警通知 DBA 处理。
3.4 知识体系总览
下面这张图总结了 XtraBackup 物理备份的核心逻辑:
一句话总结:全量备份是基础,增量备份省空间,差异备份省时间,备份集验证是底线。三者结合使用,才能构建可靠的数据库备份体系。