实验名称:Shell 脚本实现 MySQL 企业级备份(每周全量、每日增量) 适用:Linux 运维、数据库运维实训 实验环境:CentOS7 / Rocky8、MySQL5.7 或 MySQL8.0、Percona XtraBackup 实验目标:
1. 理解数据库全量备份、增量备份原理与适用场景
2. 掌握 xtrabackup 热备工具使用(无锁 InnoDB 备份)
3. 编写 Shell 脚本自动实现:每周一次全量备份,每日增量备份
4. 掌握备份恢复完整流程,理解增量备份合并规则
5. 配置 crontab 定时任务,实现自动化备份
说明:本实验采用 Percona XtraBackup(物理热备),生产和教学最常用;mysqldump 逻辑备份无法原生增量,本实验不采用。
一、实验原理
1. 全量备份(完备)
复制 InnoDB 所有数据页,生成一份完整数据库副本。本实验设定周日执行全量备份,作为本周所有增量备份的基准备份。
2. 增量备份(增备)
对比基准全量备份,只备份自上次备份之后发生变化的数据页;周一至周六每日增量,依赖本周日的全量基备。
3. XtraBackup 核心特点
支持热备份,InnoDB 引擎不锁表;备份时记录 LSN(日志序列号),增量依靠 LSN 识别变更数据。
4. 恢复逻辑
先对基备做 prepare,按时间顺序依次合并所有增量备份,最后 apply-log 完成回滚,再拷贝回数据目录。
二、实验环境准备
2.1 环境信息
项目 | 参数 |
操作系统 | CentOS7 / Rocky Linux 8 |
数据库 | MySQL 5.7 / MySQL8.0 |
备份工具 | percona-xtrabackup(2.4 版本适配 5.7,8.0 版本适配 MySQL8.0) |
备份路径 | /data/mysql_backup |
定时策略 | 每日凌晨 02:00 执行;周日 = 全量,周一~周六 = 增量 |
2.2 安装依赖工具
# CentOS7 安装xtrabackup2.4 yum install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm percona-release enable-only tools release yum install -y percona-xtrabackup-24 # Rocky8 / MySQL8.0 dnf install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm percona-release enable-only tools release dnf install -y percona-xtrabackup-80⚠️ 再次提醒:xtrabackup 是热备,MySQL 必须启动,不能 stop!
systemctl start mysqld①先装依赖(EPEL 源,必须)
yum install -y epel-release yum install -y perl-DBD-MySQL perl-Digest-MD5 libaio②下载 CentOS7 对应的 xtrabackup2.4 rpm 包
wget https://downloads.percona.com/downloads/Percona-XtraBackup-2.4/Percona-XtraBackup-2.4.28/binary/redhat/7/x86_64/percona-xtrabackup-24-2.4.28-1.el7.x86_64.rpm③本地安装(自动解决依赖)
yum localinstall -y percona-xtrabackup-24-2.4.28-1.el7.x86_64.rpm④验证
innobackupex --version输出版本号就成功!
⑤验证备份命令(单独测试)
innobackupex --user=bkuser --password=Backup@2026 /data/mysql_backup/full/2.3 MySQL 备份账号授权
登录 MySQL 执行:
mysql -u root -p[密码]
CREATE USER 'bkuser'@'localhost' IDENTIFIED BY 'Backup@2026'; GRANT RELOAD, LOCK TABLES, REPLICATION CLIENT, PROCESS ON *.* TO 'bkuser'@'localhost'; FLUSH PRIVILEGES;权限说明:
- RELOAD:刷新表
- LOCK TABLES:MyISAM 锁表备份
- REPLICATION CLIENT:获取 binlog 位置
- PROCESS:查看数据库进程
2.4 前置检查项
- MySQL 服务正常运行
- 磁盘空间充足,备份目录独立,不和数据库数据盘争抢 IO
- 关闭 SELinux(教学环境可选)
setenforce 0三、Shell 备份脚本(mysql_backup.sh)
#!/bin/bash #==================================================== # 实验脚本:MySQL XtraBackup 全量+增量自动备份【最终修复版】 # 策略:周日全量备份,周一~周六增量备份 # 日志:/data/mysql_backup/backup.log # 修复:优先使用外部传入WEEK_DAY,外部不传才自动取date #==================================================== # ----------------------配置区---------------------- MYSQL_USER="bkuser" MYSQL_PASS="Backup@2026" # MYSQL_HOST="127.0.0.1" # MYSQL_PORT="3306" BACKUP_BASE="/data/mysql_backup" LOG_FILE="${BACKUP_BASE}/backup.log" RETENTION_DAY=14 # 备份保留14天,自动清理过期 DATE=$(date +%Y%m%d_%H%M%S) FULL_DIR="${BACKUP_BASE}/full" INCR_DIR="${BACKUP_BASE}/incr" # -------------------------------------------------- # 日志输出函数 log(){ echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a ${LOG_FILE} } # 创建目录 init_dir(){ mkdir -p ${BACKUP_BASE} ${FULL_DIR} ${INCR_DIR} } # 清理过期备份 clean_backup(){ log "=== 清理${RETENTION_DAY}天前的备份文件 ===" find ${BACKUP_BASE} -type d -mtime +${RETENTION_DAY} -exec rm -rf {} \; } # 查询最新全量备份(【仅增量备份才调用这个函数!】) get_base_full(){ BASE_FULL=$(ls -td ${FULL_DIR}/*/ 2>/dev/null | head -1) if [ -z "${BASE_FULL}" ];then log "【错误】未找到基准全量备份,请先手动执行一次全量备份!" exit 1 fi } # 全量备份函数 full_backup(){ log "==== 开始执行【全量备份】 ====" FULL_PATH="${FULL_DIR}/full_${DATE}" innobackupex --user=${MYSQL_USER} --password=${MYSQL_PASS} \ --no-timestamp ${FULL_PATH} >>${LOG_FILE} 2>&1 if [ $? -eq 0 ];then log "✅ 全量备份成功,路径:${FULL_PATH}" else log "❌ 全量备份执行失败!" exit 1 fi } # 增量备份函数 incr_backup(){ get_base_full # 只有增量才去找基备! log "==== 开始执行【增量备份】,基准备份:${BASE_FULL} ====" INCR_PATH="${INCR_DIR}/incr_${DATE}" innobackupex --user=${MYSQL_USER} --password=${MYSQL_PASS} \ --incremental ${INCR_PATH} \ --incremental-basedir=${BASE_FULL} \ --no-timestamp >>${LOG_FILE} 2>&1 if [ $? -eq 0 ];then log "✅ 增量备份成功,路径:${INCR_PATH}" else log "❌ 增量备份执行失败!" exit 1 fi } # =====================主流程【重点修复!】===================== init_dir clean_backup # 核心修复:如果外部没有传入WEEK_DAY,才自动获取星期 if [ -z "${WEEK_DAY}" ];then WEEK_DAY=$(date +%w) fi if [ ${WEEK_DAY} -eq 0 ];then # 周日:直接全量,不调用get_base_full full_backup else # 工作日:增量,内部调用get_base_full校验基备 incr_backup fi log "==== 本次备份任务执行结束 ====" exit 0四、脚本部署与定时配置(实验步骤)
步骤 1:创建脚本文件
vim /data/mysql_backup/mysql_backup.sh粘贴脚本内容,保存退出。
步骤 2:添加执行权限
chmod +x /data/mysql_backup/mysql_backup.sh步骤 3:手动测试脚本(先测试周日全量,再测试增量)
# 手动强制执行全量(模拟周日) WEEK_DAY=0 /data/mysql_backup/mysql_backup.sh # 手动强制执行增量(模拟工作日) WEEK_DAY=1 /data/mysql_backup/mysql_backup.sh查看日志校验:tail -f /data/mysql_backup/backup.log
步骤 4:配置 crontab 定时任务
crontab -e写入定时规则:每日凌晨 2 点自动执行备份
0 2 * * * /data/mysql_backup/mysql_backup.sh规则解释:0 分 2 时 每天 每月 星期任意 系统自动判断星期:周日触发全量,其余日期增量
查看定时任务:
crontab -l五、备份恢复实验步骤【重点】
实验场景:周日全量备份 full_xxx,后续生成 incr1、incr2 增量备份;数据库误删,需要恢复到 incr2 时间点。 ⚠️ 恢复前提:停止 MySQL 服务,清空原有数据目录!
1. 停止 MySQL
systemctl stop mysqld2. 准备基准全量备份(加 --redo-only,用于合并增量)
innobackupex --apply-log --redo-only /data/mysql_backup/full/full_20261025_0200003. 按时间先后顺序合并增量备份(顺序不能乱!)
# 合并第一个增量 innobackupex --apply-log --redo-only /data/mysql_backup/full/full_20261025_020000 \ --incremental-dir=/data/mysql_backup/incr/incr_20261026_020000 # 合并第二个增量(最后一次合并去掉 --redo-only) innobackupex --apply-log /data/mysql_backup/full/full_20261025_020000 \ --incremental-dir=/data/mysql_backup/incr/incr_20261027_0200004. 将合并完成的备份拷贝回 MySQL 数据目录
innobackupex --copy-back /data/mysql_backup/full/full_20261025_0200005. 修改数据目录属主权限
chown -R mysql:mysql /var/lib/mysql6. 启动 MySQL,验证数据
systemctl start mysqld六、实验结果验证
1. 备份目录结构验证
/data/mysql_backup/ ├── full/ # 存放每周全量备份 ├── incr/ # 存放每日增量备份 └── backup.log # 备份日志2. 备份成功标志:备份目录内存在xtrabackup_checkpoints文件
cat /data/mysql_backup/full/full_xxxx/xtrabackup_checkpoints- full-backup-type = full:全量
- full-backup-type = incremental:增量
3. 日志查看:tail -20 /data/mysql_backup/backup.log
4. 恢复验证:登录 MySQL 核对表、数据是否还原成功
七、实验思考题(课后作业)
1. 为什么增量备份必须依赖本周的全量基备?如果基备损坏,增量备份还能用吗?
XtraBackup 增量备份只备份上一次备份(全量 / 增量)之后变化的数据页,它记录的是 LSN 增量,没有完整数据,必须以全量备份作为基础基线,一层层叠加增量数据。
✅ 基备损坏:对应的所有增量备份全部失效,无法恢复。
2. 恢复合并增量时,为什么最后一步不能加--redo-only?
--redo-only的作用:只应用 redo 日志,不回滚未提交事务,专门用于合并中间增量包(还会继续合并下一个增量)。 最后一个增量合并完成后,不再有后续增量,必须关闭--redo-only,让 xtrabackup 执行回滚未提交事务,生成一个一致性、可直接启动的实例;如果最后一步加了这个参数,数据库启动失败。
口诀:中间合并加 --redo-only,最后一次不加。
3. xtrabackup 和 mysqldump 备份有什么区别?各自适合什么场景?
对比 | xtrabackup(物理热备) | mysqldump(逻辑备份) |
备份形式 | 复制数据文件、ibd 物理页 | 导出 SQL 语句 |
锁 | InnoDB 无锁热备;MyISAM 会 FTWRL | 默认加全局读锁(5.7),单事务模式 InnoDB 可避免锁 |
速度 | 大数据量快 | 大库很慢 |
恢复 | 直接拷贝文件,恢复快 | 导入 SQL,恢复慢 |
跨版本 | 同版本最佳,跨版本兼容性差 | 兼容性好,跨版本迁移友好 |
适合场景 | 几十 G、上百 G 生产 InnoDB 大库,日常定时备份 | 小库、结构迁移、版本升级、导出单库单表、审计 |
4. 本脚本存在哪些不足?(提示:缺少异地备份、告警、备份校验)
1. 无备份有效性校验:备份完成后没有校验备份包是否损坏、没有做预恢复测试,备份成功不代表可用;
2. 无异地备份:只在本地磁盘存放备份,磁盘故障、服务器宕机则备份全部丢失;
3. 无告警机制:备份失败、磁盘满、超时没有短信 / 邮件 / 钉钉告警,故障无法及时感知;
4. 其他常见补充(面试加分):缺少磁盘空间预判、备份自动清理过期备份、备份加密、日志记录、备份超时控制。
5. 如果需要同时备份 MyISAM 引擎表,备份过程会发生什么?
MyISAM 不支持事务、没有 redo 日志。xtrabackup 备份到 MyISAM 表时,会触发FTWRL(FLUSH TABLES WITH READ LOCK),锁住所有 MyISAM 表,阻塞写入;直到 MyISAM 表文件拷贝完成之后才释放锁;InnoDB 部分依旧热备无锁。
风险:大 MyISAM 表会导致长时间锁表,业务写阻塞。