资讯动态

从mysqldump到xtrabackup:MySQL物理备份与恢复实践

发布时间:2026/10/22 11:10:54 来源:尧图企业网站定制
我们最常用的备份是mysqldump逻辑导出简单直接但数据量涨起来之后备份窗口和恢复时间会变得非常难看。我经历过一个项目业务库从几个GB涨到几十GBmysqldump导出从十几分钟变成三个多小时半夜跑的任务到第二天上午还在跑线上业务明显能感觉到IO压力。最要命的是演练恢复时20GB的逻辑备份恢复用了三个多小时真出故障根本等不起。转了一圈之后我彻底把核心备份方案切到了xtrabackup用它来承担MySQL数据库的全量备份和增量备份。这篇文章就是我自己从选型、安装、备份、恢复到脚本化的完整实践笔记不打算写成官方文档复述而是把真正用得到的命令、原理和踩过的坑讲清楚适合被备份窗口和恢复效率逼疯的后端、运维同学。1. 为什么我把核心备份方案从mysqldump换成xtrabackup1.1 物理备份和逻辑备份的差异备份这件事表面看是把数据“留下来”实际上核心问题只有两个备份要花多久恢复要花多久。mysqldump属于逻辑备份它是把表结构、数据逐条扫描出来生成一批SQL语句恢复的时候再一条条执行。数据量一大导出就慢恢复更是成倍地慢因为重建索引、约束、写入每一行数据全都得重来一遍。xtrabackup是物理备份做的事情简单粗暴直接拷贝InnoDB的数据文件。数据在磁盘上长什么样备份里就是什么样不需要重新生成SQL恢复时把文件拷回数据目录再做一次崩溃恢复就能启动。这个差别在几十GB以上的库里非常明显恢复时间能从一个量级降到另一个量级。打个比方逻辑备份像搬家时把每个抽屉里的东西写成清单到了新家再按清单把东西一件件摆回去物理备份像找搬家公司直接把整个柜子打包搬走落地就能用。我个人的做法是两者都不丢。小库、单表导出、跨大版本迁移数据我仍然用mysqldump它灵活、语句可读、还能做选择性恢复。但作为日常备份的主力在数据量上来之后我坚决换成了xtrabackup。1.2 数据量大起来之后逻辑备份先撑不住我的实际经验里逻辑备份有三个问题会随着数据量放大备份窗口过长。几十GB的库导出要数小时凌晨的备份任务经常跑到白天还在跑和业务高峰期重叠。备份期间的IO和undo日志维护会对线上写入产生干扰用--single-transaction虽然能保证一致性读但数据变更越剧烈undo快照包袱越重。恢复时间不可控。备份最终的价值是恢复。逻辑备份恢复时要执行大量INSERT加上索引重建速度比导出还慢。我经历过一次真实演练一个约20GB的库恢复用了三个多小时业务等不了这个结果基本宣判了逻辑备份在大库场景下不适合作为灾备手段。增量能力弱。mysqldump做增量只能靠binlog需要额外处理binlog的截取、归档和回放链路长、步骤多。而xtrabackup把增量备份做成了原生功能一条命令就能基于上一次备份生成增量恢复时按序合并链路短了很多。1.3 xtrabackup适合做什么不适合做什么这两年用下来我认为xtrabackup最适合这些场景全实例物理备份特别是InnoDB为主的库大数据量下的快速恢复RTO要求较高的系统需要定期增量备份缩短备份窗口快速搭建或重建从库配合binlog位点信息即可备份机环境与生产一致可直接做恢复演练但有几种情况我不会硬上xtrabackup只备份某一张表。xtrabackup虽然支持部分备份和导入导出表但链路长、易出错不如直接用mysqldump导这一张表。跨大版本迁移。xtrabackup更擅长同版本或小版本差异下的物理备份跨大版本比如5.7到8.0我仍会选择逻辑导出再导入。异构平台迁移。Linux备份到Windows这种场景物理文件没法直接通用别浪费时间。选型不是比谁功能多是匹配自己的场景。2. 安装与前置条件版本对应、最小权限和磁盘规划2.1 版本对应关系是最容易忽略的一步我第一次装xtrabackup的时候参考了一篇老教程直接装了2.4版本结果备份MySQL 8.0时报server version check failed整了半天才明白是版本对应问题。xtrabackup的版本选择和目标MySQL大版本必须对齐目标MySQL版本对应xtrabackup版本MySQL 5.6 / 5.7xtrabackup 2.4MySQL 8.0xtrabackup 8.0原因在于MySQL 8.0的数据字典、redo log格式变化很大老版本工具识别不了新格式。安装命令很简单CentOS系用包管理器安装8.0版本sudo yum install -y percona-xtrabackup-80Debian/Ubuntu系对应用apt安装同名包。装完先确认版本xtrabackup --version常见的依赖问题是libaio缺失报错一般是类似libaio.so.1: cannot open shared object file。常规包管理器会自动带依赖但如果自定义编译过基础环境就要手动补一下。这个坑很小但遇到一次会卡住半天。2.2 备份账号的最小权限配置xtrabackup不推荐直接用root账号备份建议单独建一个最小权限账号。我在MySQL 8.0里一般这样建CREATE USER backuplocalhost IDENTIFIED BY Backup2024; GRANT BACKUP_ADMIN, RELOAD, LOCK TABLES, PROCESS, REPLICATION CLIENT ON *.* TO backuplocalhost; FLUSH PRIVILEGES;这些权限各有用处BACKUP_ADMIN是为了使用MySQL 8.0的备份锁比老版本的全库只读锁更轻量RELOAD用来执行刷新类操作LOCK TABLES用于备份结束瞬间对齐非InnoDB表的状态PROCESS是观察线程REPLICATION CLIENT用于读取binlog坐标信息在搭建从库时很有用。还有一个容易被忽略的安全细节别把密码直接写在命令行里。Linux下执行ps命令能看到其他用户的完整命令行参数密码会直接暴露。我把备份账号信息放在mysql用户或root用户的~/.my.cnf里[client] userbackup passwordBackup2024同时把~/.my.cnf权限设为600然后备份命令里就不用出现密码串。命令行的可读性和安全性都更好。2.3 磁盘空间和备份目录规划备份目录的规划直接决定后面会不会出事。我踩过一次磁盘写满的坑备份跑到一半报Disk is full那一整份备份基本报废时间也浪费了。现在我的习惯是备份盘和数据盘分开避免备份IO和生产IO互相干扰磁盘空间至少预留数据实际占用量的2倍以上因为xtrabackup备份大小接近数据大小再加上增量备份、归档、临时文件余量不足迟早出事目录命名带上日期比如/backup/full_20240615_0200增量目录用/backup/inc_20240616_0200按名字排序就能看清楚备份链如果备份机和生产机不是同一台可以用xtrabackup的流式备份把备份发送到远端。全量命令加--streamtar可以打包传出去xtrabackup --backup --streamtar --parallel2 --target-dir/tmp/xtrabackup_work | gzip /backup/remote/full_20240615.tar.gz注意这里/tmp/xtrabackup_work目录必须自己提前建好且为空。流式备份一般用于异地归档日常备份我还是直接用目录方式简单可靠。3. 全量备份实操先搞懂xtrabackup的备份原理再跑命令3.1 拷文件加追redo log这就是xtrabackup的核心很多人第一次用xtrabackup时有个疑问它不锁表为什么备份出来的数据是一致的这要从InnoDB的存储机制说起。InnoDB的每个数据页上都有LSNredo log记录了所有数据变更的流水账。xtrabackup备份时并不是在同一秒拍一张快照而是做了两件事。第一件事是并行拷贝数据文件。拷贝过程中数据库还在写所以第一批拷出去的数据页可能是某个时间点的状态另一批数据页可能是稍晚一点的状态文件之间时间点参差不齐。第二件事是从备份开始就在后台持续监控和拷贝redo log所有新增的redo变更都被收集起来。备份结束时短暂获取一个轻量锁把binlog位点和非InnoDB表状态对齐很快释放。所以xtrabackup的备份本质是“一组时间点不一致的数据文件”加“一份完整的redo变更记录”。后面I do的prepare阶段会把redo日志前滚到这些数据文件上把所有文件拉到同一个一致时间点。这个机制让在线备份期间业务可以继续写入不用担心一致性。3.2 第一条全量备份命令理解了原理命令本身就不复杂了。我的日常全量备份长这样xtrabackup \ --backup \ --target-dir/backup/full_20240615_0200 \ --host127.0.0.1 \ --port3306 \ --parallel4 \ --no-server-version-check账号密码从~/.my.cnf里读所以命令行干净。这里重点解释几个参数--target-dir是备份输出目录注意这个目录必须不存在或者是一个空目录。如果你提前建好了一个有文件的目录xtrabackup会直接报错。我的习惯是不手动建目录让工具自己创建。--parallel控制并行拷贝线程数不是越大越好。我一般根据机器核数和磁盘写入能力调整常用4到8。设太大磁盘IO容易饱和反而拖慢备份速度还可能影响线上业务。--no-server-version-check这个参数要看情况加。如果确认版本匹配加上可以跳过版本检查避免个别环境下版本检测逻辑卡住。版本不匹配的情况下不建议用这个参数硬跳过恢复时风险很大。备份过程中要观察日志输出。启动时能看到类似Starting backup的提示运行中会有拷页和拷贝redo的日志最后出现completed OK才算成功。我把日志重定向到一个文件里方便事后排查xtrabackup --backup --target-dir/backup/full_20240615_0200 --parallel4 21 | tee -a /var/log/xtrabackup.log3.3 备份目录里那些文件都是干什么的全量备份跑完后目录里会多出一批xtrabackup_开头的文件不要觉得没用就删了。它们的角色很明确文件作用xtrabackup_checkpoints记录from_lsn和to_lsn是整个增量备份链的账本xtrabackup_binlog_info记录备份时刻的binlog文件名和position重建从库时要用xtrabackup_info备份工具版本、备份时间、服务器UUID等元信息backup-my.cnf备份时读取到的MySQL参数快照排查环境差异时很有用最核心的是xtrabackup_checkpoints。看这个文件就能知道备份覆盖的LSN范围cat /backup/full_20240615_0200/xtrabackup_checkpoints输出里能看到from_lsn和to_lsn。全量备份的from_lsn一般是0to_lsn是备份结束时的位置。后续增量备份就是基于这个to_lsn往下接的。所以我的习惯是每次备份完第一时间看一眼这个文件再确认日志里出现completed OK这份备份才算是真正建立起来了。如果对备份文件做异地归档推荐先看一眼备份大小是否和源库数据量级一致。差别太大说明可能缺文件得重跑。4. 全量恢复流程prepare、copy-back和权限三连4.1 为什么备份文件不能直接拷回去用有的同学第一次恢复时会想备份出来的不就是数据文件吗直接替换到数据目录启动不就行了。理论上InnoDB启动时会做崩溃恢复似乎能自动补日志但这里有个隐患。xtrabackup备份出来的数据文件之间时间点是不一致的直接启动相当于让MySQL在崩溃恢复时面对一堆时间点错乱的数据文件。运气好可能能启动但恢复到的数据时间点很混乱某些已提交事务可能丢。我们必须先用prepare把这份备份变成一份时间点一致的数据集再放回数据目录。prepare做的事情相当于提前做一次完整的崩溃恢复把redo log里的变更前滚到数据文件同时把未提交事务回滚。做完之后备份目录里的数据文件就可以直接拷走了。4.2 完整恢复步骤恢复的全流程我一般这样操作。假设目标机器的MySQL已经停止数据目录是/var/lib/mysql# 1. 停止数据库 systemctl stop mysqld # 2. 备份原数据目录而不是直接删除 mv /var/lib/mysql /var/lib/mysql_old_20240615 # 3. 创建新的空数据目录 mkdir -p /var/lib/mysql chown mysql:mysql /var/lib/mysql # 4. 对备份做prepare生成一致的数据文件 xtrabackup --prepare --target-dir/backup/full_20240615_0200 # 5. 把备份文件拷贝回数据目录 xtrabackup --copy-back --target-dir/backup/full_20240615_0200 # 6. 修正数据目录属主 chown -R mysql:mysql /var/lib/mysql # 7. 启动数据库 systemctl start mysqld第二步为什么要mv而不是rm -rf我解释一下。恢复演练时旧数据目录还留着相当于一张保命符万一新数据有问题可以立刻切回去。确认新库正常后再删掉也不迟。恢复过程中途想反悔也有退路。第五步的--copy-back会把备份数据拷贝到目标数据目录但它不负责创建目录也不负责把属主改对。所以第三步要先手动建目录第六步要再统一把属主改成mysql用户。这两个步骤少一个都不行我见过太多权限问题就是从这里来的。4.3 权限坑和空目录检查copy-back最常见的问题就是恢复完成后MySQL起不来日志里有类似Cannot open datafile或Permission denied的报错。十有八九是chown没执行或者执行得不对。跑完copy-back之后用下面命令检查一眼最稳妥ls -l /var/lib/mysql/ | head如果文件属主是root:root那mysql进程肯定没法正常读写。正确状态应该是mysql:mysql。这个坑不复杂但很容易在恢复演练时手忙脚乱地漏掉。还有一个容易被忽略的问题copy-back要求目标数据目录是空的。如果原目录没有mv走且里面有文件会有报错提示目录非空。所以先mv或至少确保目录为空再执行copy-back。5. 增量备份的正确姿势LSN账本、合并恢复与演练5.1 增量备份的原理LSN是备份链的账本增量备份是xtrabackup真正拉开差距的地方。它的原理并不复杂每一份备份目录里的xtrabackup_checkpoints都记录了to_lsn增量备份执行时去读取上一次备份的to_lsn然后扫描InnoDB数据文件里所有数据页的LSN只拷贝比这个LSN更大的页。这样一来增量目录里装的核心内容就是“自上次备份以来发生变化的数据页集合”而不是redo日志段。备份后的检查逻辑也很简单查看增量目录的from_lsn它必须等于上一次备份的to_lsn。如果不相等说明备份链已经断了需要从最近的全量重新开始。另外要特别注意增量备份的可靠性依赖InnoDB表。如果库里还有大量MyISAM表增量备份只覆盖InnoDB部分MyISAM表不会进入增量逻辑。我现在原则上要求业务表统一使用InnoDB既为了事务也为了备份链路干净。5.2 日常增量备份命令假设我有一份全量备份/backup/full_20240615_0200第一份增量的命令是这样的xtrabackup \ --backup \ --target-dir/backup/inc_20240616_0200 \ --incremental-basedir/backup/full_20240615_0200 \ --parallel4这里最关键的是--incremental-basedir它指向的是“上一份备份”而不是你想当然的基础全量。也就是说第二次增量的时候--incremental-basedir要指向第一份增量目录而不是全量目录xtrabackup \ --backup \ --target-dir/backup/inc_20240617_0200 \ --incremental-basedir/backup/inc_20240616_0200 \ --parallel4这个参数名字有迷惑性很多人第二次增量仍指向全量目录结果备份出的内容虽然没报错但链路的LSN记录是错乱的恢复时会出现重复或遗漏。判断方法就是上面说的每次增量完成后检查这份增量目录的from_lsn是否等于上一份备份的to_lsn。增量目录的大小取决于业务变更量不是固定不变的。大促、大批量更新产生的增量可能接近一次小全量所以增量备份期间同样要关注磁盘空间。5.3 增量合并恢复prepare有个特殊参数增量备份不能单独恢复必须把增量合并到全量备份上。合并的核心参数是--apply-log-only它的作用是只应用redo日志不做未提交事务的回滚。旧版本文档里常用--apply-log-only这个叫法新版里也能看到--redo-only写法意思是同一个动作碰到参数不识别就换另一个别两个同时加。合并过程按顺序执行# 第一步准备全量只做日志应用 xtrabackup --prepare --apply-log-only --target-dir/backup/full_20240615_0200 # 第二步合并第一份增量 xtrabackup --prepare --apply-log-only --target-dir/backup/full_20240615_0200 --incremental-dir/backup/inc_20240616_0200 # 第三步合并第二份增量 xtrabackup --prepare --apply-log-only --target-dir/backup/full_20240615_0200 --incremental-dir/backup/inc_20240617_0200 # 第四步最后一次完整prepare此时才允许回滚未提交事务 xtrabackup --prepare --target-dir/backup/full_20240615_0200为什么前三步不能做回滚因为回滚未提交事务会清掉事务的undo信息而增量备份里可能还有针对这些事务的后续变更一旦提前回滚后续增量数据就合不进去了。最后一步不带--apply-log-only执行完整崩溃恢复把该回滚的回滚掉数据文件才真正达到可用状态。合并完成后直接把/backup/full_20240615_0200目录做copy-back即可步骤和第4章完全一样。注意prepare是就地修改备份目录如果担心操作失误污染了原始全量可以在合并前先复制一份全量目录再操作。磁盘不紧张的前提下我个人一直保留一份未prepare的原始全量目录用来应对增量链出问题的情况。5.4 恢复演练才是真正的兜底说了这么多命令最想说的一句是没有恢复验证过的备份只是心理安慰。我现在每周固定在一个隔离的测试环境里做一次完整恢复演练把最近的全量加增量合并恢复起来启动后抽查几个关键表的数据SELECT COUNT(*) FROM orders; SELECT MAX(updated_at) FROM users;看行数和时间点是否符合预期。只要每周都能完整恢复一把备份链路基本可信。另外如果环境是用备份重建从库恢复完看xtrabackup_binlog_info文件里的binlog文件名和position执行CHANGE MASTER TO指向主库后接上复制即可这个方法在演练里也顺手就验证了。6. 把备份做成定时任务脚本骨架、保留策略和常见报错6.1 脚本骨架和防重入设计命令本身会写以后接下来是让备份“自动化”地跑起来。我的脚本设计比较简单一个bash脚本配cron调用每周日跑全量其余日期跑增量。脚本里必须加防重入机制否则上一次备份没跑完下一次任务又启动了两个xtrabackup进程同时操作会把LSN链搞乱。用flock做互斥锁很直接#!/bin/bash set -e exec 9/var/lock/xtrabackup.lock flock -n 9 || { echo another backup is still running /var/log/xtrabackup.log; exit 1; } BACKUP_BASE/backup DATE_TAG$(date %Y%m%d) LOG_FILE/var/log/xtrabackup.log COMMON_OPTS--backup --host127.0.0.1 --port3306 --parallel4 if [ $(date %u) -eq 7 ]; then TARGET_DIR$BACKUP_BASE/full_$DATE_TAG CMDxtrabackup $COMMON_OPTS --target-dir$TARGET_DIR else LAST_DIR$(ls -dt $BACKUP_BASE/full_* | head -1) if [ -z $LAST_DIR ]; then echo no full backup found $LOG_FILE exit 1 fi TARGET_DIR$BACKUP_BASE/inc_$DATE_TAG CMDxtrabackup $COMMON_OPTS --target-dir$TARGET_DIR --incremental-basedir$LAST_DIR fi echo start: $CMD $LOG_FILE $CMD $LOG_FILE 21 echo finished at $(date) $LOG_FILE这里有个细节脚本里判断上一份备份用的是ls -dt取最新的full目录这只适合简化场景。真正要严谨的话应该读取上一份备份的checkpoints文件判断它是否已经存在、是否有效再决定增量起点。否则万一全量备份本身是坏的增量接上去全链报废。cron配置示例0 2 * * * /opt/scripts/xtrabackup_backup.sh每天凌晨2点跑周日是全量其他是增量。6.2 保留策略与清理规则备份不是越多越好要按保留策略清理。我的规则是全量保存2周增量保存最近一轮备份周期的内容find /backup -maxdepth 1 -name full_* -mtime 14 -exec rm -rf {} \; find /backup -maxdepth 1 -name inc_* -mtime 7 -exec rm -rf {} \;清理前我会确认当前仍存在至少一份可用的全量备份。如果全量还没有成功过清理脚本千万别动存量目录。所以我在清理命令前加一个判断如果/backup下面没有full_目录跳过清理同时告警。备份脚本本身在cron里跑失败时要有感知。我的做法是脚本中xtrabackup命令失败时给通知机器人发一条包含日志文件路径的消息简单直接。6.3 生产中常碰到的报错与排查把各种报错按现象列出来排查时会快很多报错现象常见原因处理方式libaio.so.1: cannot open shared object file缺少libaio依赖安装libaio相关包Directory ... exists but is not emptytarget-dir已存在且有文件换新目录或清空后重试server version check failedxtrabackup版本与MySQL版本不对应换对应大版本工具或加--no-server-version-checkDisk is full备份磁盘空间不足清理旧备份拆分备份目录换更大盘copy-back后MySQL无法启动Permission denied数据目录属主不对chown -R mysql:mysql /var/lib/mysql增量备份的from_lsn和上一份to_lsn不相等--incremental-basedir指错或链路断从最近全量重新构建备份链prepare阶段报错crash recovery failed备份文件损坏或增量合并顺序错乱用未prepare的原始全量重新合并检查磁盘空间排查任何备份问题时我习惯先看日志尾部200行再看checkpoints文件里的LSN字段两手一起抓。xtrabackup的日志一般直接打到stderr所以脚本里一定记得重定向到文件否则cron环境下问题出在哪都找不到。最后再分享一个习惯。我做完任何一次恢复演练都会把演练用的命令和结果简单记到备份目录旁边的一个文本文件里下次演练时直接照着自己上次成功的操作来做减少临时拼命令的出错概率。xtrabackup这类物理备份工具真正难的不是命令本身而是你能不能坚持每周验证、每份备份都保持链路完整。备份这件事练得越多心里越有底。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑