MySQL 物理备份与恢复:xtrabackup 实战
mysqldump 是逻辑备份,恢复时要重放 SQL,大库恢复动辄数小时,生产上等不起。物理备份直接拷数据文件,恢复速度是数量级的差别:Percona XtraBackup(PXB)是 MySQL 物理备份的事实标准,支持热备、全量与增量、流式压缩,本文以 MySQL 8.0 为例,完整走通备份、恢复与校验。
先说适用边界:XtraBackup 只支持 InnoDB/XtraDB 引擎的热备,MyISAM 表备份时会锁表;备份期间不要做 DDL(加表、改表结构),否则备份可能不一致;恢复的目标实例版本要与备份源一致或更高。
一、安装与备份账号准备
备份账号只要最小权限:备份、复制、锁表与查看进程,不要用 root 跑备份。安装 PXB 时版本必须与 MySQL 大版本对应,8.0 的库用 PXB 8.0,装错版本备份会直接报错。
# 创建备份专用账号(MySQL 8.0)
mysql -uroot -p -e "CREATE USER 'bkp'@'localhost' IDENTIFIED BY 'BkpPass_2026';
GRANT RELOAD, LOCK TABLES, PROCESS, REPLICATION CLIENT, BACKUP_ADMIN ON *.* TO 'bkp'@'localhost';
FLUSH PRIVILEGES;"
# 安装(以 RHEL 8 为例,版本与 MySQL 8.0 对应)
yum install -y percona-xtrabackup-80
xtrabackup --version
# 备份目录规划:全量与增量分开,保留期按恢复演练结果定
mkdir -p /backup/mysql/{full,inc}
二、全量备份:热备的完整流程
XtraBackup 的热备分两步:先拷数据文件(期间数据库正常读写),再在备份结束瞬间对 redo log 做一致性点处理。备份完成后必须做 --prepare 预处理,把 redo log 回放到数据文件,备份才处于一致状态——没 prepare 的备份是不能直接恢复的。
# 全量备份(流式压缩,直接落盘)
xtrabackup --backup --user=bkp --password='BkpPass_2026' \
--target-dir=/backup/mysql/full/2026-11-09 \
--compress --compress-threads=4 --parallel=4
# 备份完成后立刻 prepare(否则恢复时会报数据不一致)
xtrabackup --prepare --target-dir=/backup/mysql/full/2026-11-09
# 检查备份是否可用:看 xtrabackup_checkpoints,backup_type 应为 full-backuped
cat /backup/mysql/full/2026-11-09/xtrabackup_checkpoints
ls -lh /backup/mysql/full/2026-11-09/ | head
备份时间窗口要实测:全量备份的耗时与数据量、磁盘 I/O 直接相关,1TB 的库在 SATA 盘上可能要数小时,务必在业务低峰期跑,并监控备份期间的 I/O 争抢。
三、增量备份:基于 LSN 的增量链
增量备份只拷上次备份之后变化的页,依据是 LSN(日志序列号)。增量链的恢复顺序是:全量 prepare → 依次应用每个增量 → 最后一次 prepare。链上任何一环丢失,之后的增量都作废,所以增量备份要和全量备份一起做保留期管理,不要只留增量。
# 增量备份(基于上一次全量)
xtrabackup --backup --user=bkp --password='BkpPass_2026' \
--target-dir=/backup/mysql/inc/2026-11-09-inc1 \
--incremental-basedir=/backup/mysql/full/2026-11-09 \
--compress --parallel=4
# 恢复前合并:先 prepare 全量(不提交 redo),再依次应用增量
xtrabackup --prepare --apply-log-only --target-dir=/backup/mysql/full/2026-11-09
xtrabackup --prepare --apply-log-only --target-dir=/backup/mysql/full/2026-11-09 \
--incremental-dir=/backup/mysql/inc/2026-11-09-inc1
xtrabackup --prepare --target-dir=/backup/mysql/full/2026-11-09
四、恢复:拷回数据目录并修正权限
恢复的本质是把 prepare 好的数据文件拷回 MySQL 的 datadir。关键点:目标 datadir 必须为空、恢复后权限必须是 mysql:mysql、恢复完要重启实例并检查错误日志。不要直接在原库上恢复演练,演练一律用独立的恢复实例。
# 停掉目标实例,清空 datadir(先确认这是恢复实例,不是生产库)
systemctl stop mysqld
rm -rf /var/lib/mysql/*
# 拷回数据文件
xtrabackup --copy-back --target-dir=/backup/mysql/full/2026-11-09
chown -R mysql:mysql /var/lib/mysql
# 启动并验证
systemctl start mysqld
tail -50 /var/log/mysqld.log
mysql -uroot -p -e "SELECT COUNT(*) FROM information_schema.tables; SHOW MASTER STATUS;"
五、时间点恢复:全量 + binlog 的组合打法
物理备份只能恢复到备份那个时间点,要恢复到“误删表之前的那一分钟”,还得靠 binlog。流程:先用全量备份恢复到备份点,再用 mysqlbinlog 按时间范围重放 binlog 到误操作之前。binlog 必须单独备份、与数据备份分开存放,否则备份本身丢了,时间点恢复无从谈起。
<pre class="wp-block-syntaxhighlighter-code"># 找到误删前的时间点(先查 binlog 确认误操作的位置)
mysqlbinlog --start-datetime='2026-11-09 10:00:00' \
--stop-datetime='2026-11-09 10:29:59' \
/backup/mysql/binlog/mysql-bin.000123 > /tmp/pitr.sql
# 先恢复全量备份(见第四节),再重放 binlog
mysql -uroot -p < /tmp/pitr.sql
# 验证误删的表是否回来、数据是否停在误操作前
mysql -uroot -p -e "SELECT COUNT(*) FROM app.orders WHERE created_at < '2026-11-09 10:30:00';"</pre>
六、流式备份与远程备份:备份直接落到远端
本地磁盘空间紧张时,全量备份没必要先落本地再传走:xbstream 流式输出可以边备份边压缩、边传到备份服务器,本地只留极小临时空间。流式备份要配合同步校验——流断了、传丢了,备份目录看着完整实际是坏的,所以传完一定要在远端做一次解包校验。
<pre class="wp-block-syntaxhighlighter-code"># 流式备份 + 压缩,直接传到备份服务器(本地不落盘)
xtrabackup --backup --stream=xbstream --compress --compress-threads=4 \
--target-dir=/tmp/stream-out 2>/tmp/backup.log | \
ssh backup@backup-host "cat > /backup/mysql/full-$(date +%F).xbstream"
# 远端解包并校验(不要只看文件大小)
# ssh backup@backup-host "cd /backup/mysql && xbstream -x < full-2026-11-09.xbstream"
# 解压后再做 prepare,确认备份可用
# xtrabackup --prepare --target-dir=/backup/mysql/full-2026-11-09
# 加密备份:备份服务器不可信时再加一层
# xtrabackup --backup --stream=xbstream --encrypt=AES256 \
# --encrypt-key-file=/etc/mysql/backup-key --target-dir=/tmp/stream-out
# 备份日志里看关键行:completed OK 才算成功
grep -E 'completed OK|error' /tmp/backup.log</pre>
流式备份的坑主要在两处:一是网络中断后重试要从头开始,大库备份前先确认网络稳定,必要时走内网专线;二是压缩线程数不要超过备份机 CPU 的一半,压缩抢 CPU 会拖慢主库的正常查询。备份密钥文件权限设 600,单独保管,丢了密钥等于丢了所有加密备份。
七、单表恢复:不用全库恢复就能救一张表
误删一张表时,全库恢复代价太大:停机时间长,还可能丢掉误删之后的新数据。xtrabackup 的单表恢复可以只导出那张表,再导回目标库,适合“只坏一张表”的场景。前提是备份时用了 --export,且表是独立表空间(innodb_file_per_table),建表时没开这个选项的表走不了这条路。
# 1. 备份时导出单表元数据(全量备份命令里加上 --export)
# xtrabackup --backup --export --target-dir=/backup/mysql/full-2026-11-09
# 2. 对要恢复的表做 prepare(只准备这一张表)
xtrabackup --prepare --export --target-dir=/backup/mysql/full-2026-11-09
# 3. 在目标库上建同名空表(表结构要与备份时一致)
# mysql -e "USE appdb; CREATE TABLE orders (...);" # 结构从备份的 .cfg/.exp 恢复
# 4. 丢弃空表空间,再把备份的表空间文件拷回来并导入
mysql -e "USE appdb; ALTER TABLE orders DISCARD TABLESPACE;"
cp /backup/mysql/full-2026-11-09/appdb/orders.{ibd,cfg,exp} /var/lib/mysql/appdb/
chown mysql:mysql /var/lib/mysql/appdb/orders.*
mysql -e "USE appdb; ALTER TABLE orders IMPORT TABLESPACE;"
# 5. 校验行数与关键数据
mysql -e "USE appdb; SELECT COUNT(*) FROM orders;"
单表恢复有三个限制:分区表、外键关联多的表、以及备份后表结构被改过的情况,都容易导入失败,失败时再回落到全库恢复。操作前先在测试库完整走一遍,确认 .cfg 文件与当前表结构匹配。恢复完要检查外键与索引是否完整,不要只看行数。
八、用备份快速重建从库:比重新搭主从快得多
从库坏了、或者要扩一个新从库时,从主库重新全量同步往往要等很久。用主库的 xtrabackup 全量备份直接起一个从库,是最常用的重建方式:备份自带主库当时的 GTID 位点,恢复后从库直接从这个位点开始追,不用重新导数据。
# 1. 在主库做全量备份并 prepare(备份目录要拷到新从库)
# xtrabackup --backup --target-dir=/backup/mysql/full-2026-11-09
# xtrabackup --prepare --target-dir=/backup/mysql/full-2026-11-09
# 2. 在新从库上拷回数据目录并修正权限
# rsync -av /backup/mysql/full-2026-11-09/ new-slave:/var/lib/mysql/
# chown -R mysql:mysql /var/lib/mysql && systemctl start mysqld
# 3. 从备份里取 GTID 位点(xtrabackup_binlog_info)
cat /var/lib/mysql/xtrabackup_binlog_info
# 4. 在从库上设置复制源为该位点并启动复制
mysql -e "SET GLOBAL gtid_purged='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:1-12345';"
mysql -e "CHANGE MASTER TO MASTER_HOST='master-host', MASTER_USER='repl', \
MASTER_PASSWORD='repl-password', MASTER_AUTO_POSITION=1;"
mysql -e "START SLAVE; SHOW SLAVE STATUS\G" | grep -E 'Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master'
重建从库最容易踩的坑是 GTID 位点取错:位点必须来自 xtrabackup_binlog_info,不要凭感觉填。启动复制后先看 Seconds_Behind_Master 是否在缩小,确认追上后再把读流量切过去。备份期间主库的 binlog 保留期要够长,否则从库还没追上、主库已经把需要的 binlog 清理了,只能重新备份。
九、备份校验与恢复演练清单
没演练过的备份等于没有备份。每月至少做一次完整恢复演练,演练清单:
- 备份完整性:每次备份后检查
xtrabackup_checkpoints与备份目录大小,异常立即告警,不要等恢复时才发现备份是坏的。 - 恢复演练:在独立实例上完整恢复一次,核对表数量、行数抽查、应用能否正常连接,记录恢复耗时(RTO 的真实值)。
- 保留期:全量至少保留 2 份、增量链完整保留,异地再存一份;保留期到期前先确认新备份已成功,再删旧备份。
- 加密与权限:备份目录权限 700,含密码的备份脚本不要进版本库;异地备份走加密传输。
- 监控项:备份任务是否按时完成、备份大小是否异常突变、binlog 备份是否连续无断点。







