MySQL备份恢复全攻略:mysqldump与xtrabackup实战(DBA救命指南)
对于DBA来说,数据库备份就像程序员的「Ctrl+S」——平时不起眼,出事了能救命,而且是最重要的救命手段,没有之一。相信很多DBA都有过这样的惊魂时刻:开发手滑,一条没有WHERE条件的DELETE语句下去,用户表瞬间清空;服务器宕机,数据目录损坏,眼看业务停摆,领导在身后催得焦头烂额……
我至今还记得2018年的那次事故:开发误删用户全量数据,当时只有每天凌晨1次的全量备份,事故发生在下午5点,我们硬生生靠binlog恢复了4个小时,业务停摆半天,损失惨重。
从那以后,我们彻底重构了备份策略——从“裸奔式”每日全备,升级为「全量备份+增量备份+binlog归档」的黄金组合,RTO(恢复时间目标)从4小时压缩到30分钟,RPO(恢复点目标)从24小时缩减到5分钟,再也不用为数据丢失夜不能寐。
今天,就把这份沉淀了多年的MySQL备份恢复实战攻略,毫无保留分享给大家——涵盖mysqldump和xtrabackup两大核心工具,从基础用法到生产实战,从故障排查到最佳实践,新手能上手,老手能避坑。
一、先搞懂:两大备份工具,该选谁?
MySQL备份工具千千万,但生产环境最常用、最靠谱的,就两个:mysqldump 和 Percona XtraBackup。它们就像两把“双刃剑”,各有专攻,选对了能省90%的麻烦。
核心结论:小型数据库选mysqldump(简单省心),大型数据库选XtraBackup(高效低影响),跨版本迁移必用mysqldump。
1.1 mysqldump:MySQL官方“轻量选手”
mysqldump是MySQL自带的逻辑备份工具,说白了就是“把数据库里的数据,导出成SQL语句”,就像把一本书的内容逐字抄下来,可读性强,操作简单。
特性详细说明备份类型逻辑备份(导出SQL语句,肉眼可看)锁机制默认锁表,可用--single-transaction实现InnoDB一致性读(不锁表)速度较慢(要逐行查询、生成SQL),大数据量会“卡壳”适用场景小型数据库(50GB)、生产环境低影响备份优点快速、低影响、支持增量备份,恢复速度快缺点只支持同版本恢复,配置比mysqldump复杂,需要额外安装1.3 场景对应表(直接抄作业)
不用纠结,对照下面的场景选工具,准没错:
应用场景推荐工具备份策略开发测试环境mysqldump每日全备(简单省事)小型生产(500GB)XtraBackup每周全备 + 每日增量 + 流式备份跨版本迁移mysqldump逻辑导出导入(兼容性强)表级恢复mysqldump单表导出(精准恢复,不影响其他表)时间点恢复(PITR)XtraBackup + binlog全备 + binlog应用(恢复到任意时间点)1.4 环境要求(必看,避免踩坑)
环境不匹配,备份必失败!提前核对以下版本和配置:
组件版本要求关键说明MySQL Server8.0.35+ 或 8.4 LTS稳定版优先,避免使用测试版Percona XtraBackup8.0.35+版本必须与MySQL匹配(比如MySQL 8.0.35,XtraBackup也用8.0.35)操作系统Rocky 9 / Ubuntu 24.04生产环境推荐,兼容性最好磁盘空间数据量的2-3倍预留备份、压缩、恢复的空间,避免空间不足导致备份失败qpress最新版XtraBackup压缩备份必需工具二、实战操作:从准备到备份,一步到位
铺垫再多,不如动手操作。这部分是核心,每一步都有详细命令,直接复制粘贴就能用(记得替换自己的密码和目录)。
2.1 准备工作(3步搞定,避免后续踩坑)
2.1.1 安装备份工具
分两种操作系统,按需选择:
✅ Rocky Linux 9 / CentOS Stream 9
# 安装Percona仓库(XtraBackup需要)
sudo dnf install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm
sudo percona-release setup pxb-80
# 安装XtraBackup
sudo dnf install -y percona-xtrabackup-80
# 安装压缩工具(备份压缩必需)
sudo dnf install -y qpress lz4
# 验证安装(出现版本号即成功)
xtrabackup --version
# 正常输出:xtrabackup version 8.0.35-30 based on MySQL server 8.0.35✅ Ubuntu 24.04
sudo apt-get update
sudo apt-get install -y wget gnupg2 lsb-release
wget https://repo.percona.com/apt/percona-release_latest.generic_all.deb
sudo dpkg -i percona-release_latest.generic_all.deb
sudo percona-release setup pxb-80
sudo apt-get install -y percona-xtrabackup-80 qpress2.1.2 创建备份专用用户
不要用root用户备份!创建专用备份用户,分配最小权限,更安全:
-- 登录MySQL,执行以下SQL
CREATE USER 'backup'@'localhost' IDENTIFIED BY 'BackupPass@2024';-- 密码替换成自己的
-- mysqldump所需权限(复制粘贴即可)
GRANT SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT ON *.* TO 'backup'@'localhost';
-- XtraBackup所需权限(复制粘贴即可)
GRANT RELOAD, PROCESS, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'backup'@'localhost';
GRANT BACKUP_ADMIN ON *.* TO 'backup'@'localhost';-- MySQL 8.0+ 必需
GRANT SELECT ON performance_schema.log_status TO 'backup'@'localhost';
GRANT SELECT ON performance_schema.keyring_component_status TO 'backup'@'localhost';
FLUSH PRIVILEGES;-- 刷新权限,立即生效2.1.3 规划备份目录
目录混乱会导致后续恢复找不到文件,建议按以下结构创建,一目了然:
# 创建备份目录结构(一次性执行)
sudo mkdir -p /backup/mysql/{full,incremental,binlog,scripts,logs}
sudo chown -R mysql:mysql /backup/mysql# 授权mysql用户,避免权限不足
sudo chmod 750 /backup/mysql# 限制访问权限,更安全
# 目录说明(记好,后续用得到):
# /backup/mysql/full - 存放全量备份
# /backup/mysql/incremental - 存放增量备份
# /backup/mysql/binlog - 存放binlog归档
# /backup/mysql/scripts - 存放备份脚本
# /backup/mysql/logs - 存放备份日志
# 重要提醒:备份目录建议用独立磁盘或NFS挂载,避免和数据目录在同一磁盘(防止磁盘损坏一起丢)2.2 核心操作:mysqldump实战用法
适合小型数据库,操作简单,以下是最常用的几种场景,覆盖90%的需求:
✅ 备份单个数据库
mysqldump -u backup -p'BackupPass@2024' \
--single-transaction \# InnoDB不锁表,关键参数
--routines \ # 备份存储过程和函数
--triggers \ # 备份触发器
--events \ # 备份事件调度器
mydb > /backup/mysql/full/mydb_$(date +%Y%m%d).sql# 备份到指定目录,带日期✅ 备份多个数据库
mysqldump -u backup -p'BackupPass@2024' \
--single-transaction \
--routines \
--triggers \
--databases db1 db2 db3 > /backup/mysql/full/multi_db_$(date +%Y%m%d).sql✅ 备份所有数据库(最常用)
mysqldump -u backup -p'BackupPass@2024' \
--single-transaction \
--routines \
--triggers \
--events \
--all-databases > /backup/mysql/full/all_db_$(date +%Y%m%d).sql✅ 备份单个表(精准恢复,不影响其他表)
mysqldump -u backup -p'BackupPass@2024' \
--single-transaction \
mydb users > /backup/mysql/full/mydb_users_$(date +%Y%m%d).sql# mydb是库名,users是表名✅ 生产环境推荐参数组合(带压缩,更省空间)
这是我平时用的配置,兼顾安全和效率,直接复制用:
mysqldump -u backup -p'BackupPass@2024' \
--single-transaction \ # InnoDB一致性读,不锁表(必需)
--source-data=2 \ # 记录binlog位置(注释形式,方便恢复)
--routines \ # 包含存储过程和函数
--triggers \ # 包含触发器
--events \ # 包含事件调度器
--set-gtid-purged=AUTO \ # GTID自动处理(MySQL 8.0+ 推荐)
--hex-blob \ # 二进制数据用十六进制,避免乱码
--quick \ # 逐行读取,减少内存占用
--max-allowed-packet=512M \ # 支持大数据包,避免导出失败
--default-character-set=utf8mb4 \# 字符集,避免乱码
--all-databases \
| gzip > /backup/mysql/full/all_db_$(date +%Y%m%d).sql.gz# 压缩备份,省空间注意:MySQL 8.0.26+ 版本,--master-data 参数已改为 --source-data,别用错了!
2.3 核心操作:XtraBackup实战用法
适合大型数据库,热备份、速度快,重点掌握全量和增量备份(生产环境最常用)。
✅ 全量备份(基础,增量备份的前提)
# 基础全量备份
xtrabackup --backup \
--user=backup \
--password='BackupPass@2024' \
--target-dir=/backup/mysql/full/$(date +%Y%m%d)# 备份目录带日期,方便区分
# 备份并压缩(推荐,省空间)
xtrabackup --backup \
--user=backup \
--password='BackupPass@2024' \
--target-dir=/backup/mysql/full/$(date +%Y%m%d) \
--compress \ # 开启压缩
--compress-threads=4 # 4线程压缩,提升速度
# 流式备份到远程(大型库推荐,避免本地磁盘不够)
xtrabackup --backup \
--user=backup \
--password='BackupPass@2024' \
--stream=xbstream \ # 流式输出
--compress \
| ssh backup@remote-server "cat > /backup/mysql/full_$(date +%Y%m%d).xbstream"# 传输到远程服务器✅ 增量备份(省空间,适合大型库)
增量备份只备份上次备份后变化的数据,比全量备份小很多,步骤如下(必须基于全量备份):
# 第一步:创建全量备份(基准备份,只做一次)
xtrabackup --backup \
--user=backup \
--password='BackupPass@2024' \
--target-dir=/backup/mysql/full/base
# 第二步:创建第一次增量备份(基于全量备份)
xtrabackup --backup \
--user=backup \
--password='BackupPass@2024' \
--target-dir=/backup/mysql/incremental/inc1 \
--incremental-basedir=/backup/mysql/full/base# 指定基准备份目录
# 第三步:创建第二次增量备份(基于第一次增量)
xtrabackup --backup \
--user=backup \
--password='BackupPass@2024' \
--target-dir=/backup/mysql/incremental/inc2 \
--incremental-basedir=/backup/mysql/incremental/inc1# 指定上一次增量备份目录✅ 备份准备与恢复(关键,备份了不能恢复等于白做)
XtraBackup备份后不能直接恢复,必须先“准备”(应用redo log,确保数据一致性):
# 1. 准备全量备份(基准备份)
xtrabackup --prepare \
--apply-log-only \# 只应用redo log,不做其他操作(增量备份必需)
--target-dir=/backup/mysql/full/base
# 2. 应用第一次增量备份到基准备份
xtrabackup --prepare \
--apply-log-only \
--target-dir=/backup/mysql/full/base \
--incremental-dir=/backup/mysql/incremental/inc1
# 3. 应用第二次增量备份(最后一次增量,不用加--apply-log-only)
xtrabackup --prepare \
--target-dir=/backup/mysql/full/base \
--incremental-dir=/backup/mysql/incremental/inc2
# 4. 最终准备(确保数据完整)
xtrabackup --prepare \
--target-dir=/backup/mysql/full/base
# 恢复备份(停止MySQL后执行)
xtrabackup --copy-back \
--target-dir=/backup/mysql/full/base
# 恢复后设置权限(必须,否则MySQL启动失败)
chown -R mysql:mysql /data/mysql/data# /data/mysql/data是你的MySQL数据目录三、生产实战:脚本+案例,直接落地
手动执行备份太麻烦,而且容易忘,生产环境一定要用脚本+定时任务,实现自动化备份。另外,附上3个高频故障恢复案例,帮你应对突发情况。
3.1 生产环境备份脚本(直接复制可用)
✅ mysqldump备份脚本(小型数据库)
文件路径:/backup/mysql/scripts/mysqldump_backup.sh,记得替换配置中的密码和目录:
#!/bin/bash
# 文件:/backup/mysql/scripts/mysqldump_backup.sh
# 功能:生产环境mysqldump自动化备份脚本
# 用法:./mysqldump_backup.sh
set -e# 执行出错立即退出
# 配置(按需修改)
MYSQL_USER="backup"
MYSQL_PASS="BackupPass@2024"# 替换成你的备份用户密码
MYSQL_HOST="localhost"
BACKUP_DIR="/backup/mysql/full"
LOG_DIR="/backup/mysql/logs"
RETENTION_DAYS=7# 备份保留7天,自动清理
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="${LOG_DIR}/mysqldump_${DATE}.log"
# 备份元数据库(记录备份信息,可选)
META_DB="backup_meta"
RECORD_BACKUP=true
# 初始化目录
mkdir -p ${BACKUP_DIR} ${LOG_DIR}
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a ${LOG_FILE}
}
# 记录备份结果到元数据库
record_backup() {
local status=$1
local error_msg=$2
local end_time=$(date '+%Y-%m-%d %H:%M:%S')
local backup_size=$(stat -c%s ${BACKUP_FILE} 2>/dev/null || echo 0)
local compressed_size=$(stat -c%s ${BACKUP_FILE}.gz 2>/dev/null || echo 0)
if [ "$RECORD_BACKUP" = true ]; then
mysql -u${MYSQL_USER} -p${MYSQL_PASS} ${META_DB} << EOF
UPDATE backup_history SET
end_time = '${end_time}',
duration_seconds = TIMESTAMPDIFF(SECOND, start_time, '${end_time}'),
status = '${status}',
backup_size_bytes = ${backup_size},
compressed_size_bytes = ${compressed_size},
error_message = '${error_msg}'
WHERE id = ${BACKUP_ID};
EOF
fi
}
# 全量备份
do_full_backup() {
log "Starting full backup..."
BACKUP_FILE="${BACKUP_DIR}/all_databases_${DATE}.sql"
# 获取binlog位置(用于时间点恢复)
BINLOG_INFO=$(mysql -u${MYSQL_USER} -p${MYSQL_PASS} -N -e "SHOW MASTER STATUS")
BINLOG_FILE=$(echo $BINLOG_INFO | awk '{print $1}')
BINLOG_POS=$(echo $BINLOG_INFO | awk '{print $2}')
GTID_EXECUTED=$(mysql -u${MYSQL_USER} -p${MYSQL_PASS} -N -e "SELECT @@global.gtid_executed" | tr '\n' ' ')
# 记录备份开始
if [ "$RECORD_BACKUP" = true ]; then
BACKUP_ID=$(mysql -u${MYSQL_USER} -p${MYSQL_PASS} ${META_DB} -N -e "
INSERT INTO backup_history (backup_type, backup_tool, backup_path, start_time, binlog_file, binlog_position, gtid_executed)
VALUES ('full', 'mysqldump', '${BACKUP_FILE}', NOW(), '${BINLOG_FILE}', ${BINLOG_POS}, '${GTID_EXECUTED}');
SELECT LAST_INSERT_ID();
")
fi
# 执行备份
log "Backup file: ${BACKUP_FILE}"
log "Binlog: ${BINLOG_FILE}:${BINLOG_POS}"
mysqldump -u${MYSQL_USER} -p${MYSQL_PASS} -h${MYSQL_HOST} \
--single-transaction \
--source-data=2 \
--routines \
--triggers \
--events \
--set-gtid-purged=AUTO \
--hex-blob \
--quick \
--max-allowed-packet=512M \
--default-character-set=utf8mb4 \
--all-databases > ${BACKUP_FILE} 2>>${LOG_FILE}
if [ $? -eq 0 ]; then
log "Backup completed successfully"
# 压缩备份
log "Compressing backup..."
gzip ${BACKUP_FILE}
log "Compressed size: $(du -h ${BACKUP_FILE}.gz | cut -f1)"
record_backup "success" ""
else
log "Backup FAILED!"
record_backup "failed" "mysqldump failed"
exit 1
fi
}
# 单库备份
do_single_backup() {
local DB_NAME=$1
log "Starting single database backup: ${DB_NAME}"
BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}_${DATE}.sql"
mysqldump -u${MYSQL_USER} -p${MYSQL_PASS} -h${MYSQL_HOST} \
--single-transaction \
--source-data=2 \
--routines \
--triggers \
--set-gtid-purged=AUTO \
--hex-blob \
--quick \
${DB_NAME} > ${BACKUP_FILE} 2>>${LOG_FILE}
if [ $? -eq 0 ]; then
log "Single database backup completed: ${DB_NAME}"
gzip ${BACKUP_FILE}
else
log "Single database backup FAILED: ${DB_NAME}"
exit 1
fi
}
# 清理旧备份(保留7天)
cleanup_old_backups() {
log "Cleaning up backups older than ${RETENTION_DAYS} days..."
find ${BACKUP_DIR} -name "*.sql.gz" -mtime +${RETENTION_DAYS} -delete
find ${LOG_DIR} -name "*.log" -mtime +${RETENTION_DAYS} -delete
log "Cleanup completed"
}
# 主程序
case "$1" in
full)
do_full_backup
;;
single)
if [ -z "$2" ]; then
log "Error: Database name required"
exit 1
fi
do_single_backup $2
;;
*)
do_full_backup# 默认执行全量备份
;;
esac
cleanup_old_backups
log "Backup script completed"✅ XtraBackup备份脚本(大型数据库)
文件路径:/backup/mysql/scripts/xtrabackup_backup.sh,支持全量和增量备份:
#!/bin/bash
# 文件:/backup/mysql/scripts/xtrabackup_backup.sh
# 功能:生产环境XtraBackup自动化备份脚本
# 用法:./xtrabackup_backup.sh
set -e
# 配置(按需修改)
MYSQL_USER="backup"
MYSQL_PASS="BackupPass@2024"
BACKUP_BASE="/backup/mysql"
FULL_BACKUP_DIR="${BACKUP_BASE}/full"
INCR_BACKUP_DIR="${BACKUP_BASE}/incremental"
LOG_DIR="${BACKUP_BASE}/logs"
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="${LOG_DIR}/xtrabackup_${DATE}.log"
COMPRESS_THREADS=4# 压缩线程数
PARALLEL_THREADS=4# 并行备份线程数
# 保留策略
FULL_RETENTION_DAYS=30# 全量备份保留30天
INCR_RETENTION_DAYS=7 # 增量备份保留7天
# 初始化目录
mkdir -p ${FULL_BACKUP_DIR} ${INCR_BACKUP_DIR} ${LOG_DIR}
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a ${LOG_FILE}
}
# 获取最新全量备份
get_latest_full_backup() {
ls -td ${FULL_BACKUP_DIR}/*/ 2>/dev/null | head -1
}
# 获取最新增量备份
get_latest_incremental() {
local base_dir=$1
local base_name=$(basename $base_dir)
ls -td ${INCR_BACKUP_DIR}/${base_name}_inc*/ 2>/dev/null | head -1
}
# 全量备份
do_full_backup() {
log "Starting full backup..."
local BACKUP_PATH="${FULL_BACKUP_DIR}/${DATE}"
xtrabackup --backup \
--user=${MYSQL_USER} \
--password=${MYSQL_PASS} \
--target-dir=${BACKUP_PATH} \
--parallel=${PARALLEL_THREADS} \
--compress \
--compress-threads=${COMPRESS_THREADS} \
2>>${LOG_FILE}
if [ $? -eq 0 ]; then
log "Full backup completed: ${BACKUP_PATH}"
cat ${BACKUP_PATH}/xtrabackup_checkpoints | tee -a ${LOG_FILE}
local SIZE=$(du -sh ${BACKUP_PATH} | cut -f1)
log "Backup size: ${SIZE}"
else
log "Full backup FAILED!"
exit 1
fi
}
# 增量备份
do_incremental_backup() {
log "Starting incremental backup..."
# 获取基准目录(没有全量备份则自动执行全量)
local LATEST_FULL=$(get_latest_full_backup)
if [ -z "$LATEST_FULL" ]; then
log "No full backup found, performing full backup instead"
do_full_backup
return
fi
local FULL_NAME=$(basename $LATEST_FULL)
log "Base full backup: ${LATEST_FULL}"
# 检查是否有之前的增量备份
local LATEST_INCR=$(get_latest_incremental $LATEST_FULL)
local BASE_DIR=$LATEST_FULL
if [ -n "$LATEST_INCR" ]; then
BASE_DIR=$LATEST_INCR
log "Base incremental backup: ${LATEST_INCR}"
fi
# 生成增量备份名称
local INCR_COUNT=$(ls -d ${INCR_BACKUP_DIR}/${FULL_NAME}_inc*/ 2>/dev/null | wc -l)
local INCR_NAME="${FULL_NAME}_inc$((INCR_COUNT + 1))_${DATE}"
local BACKUP_PATH="${INCR_BACKUP_DIR}/${INCR_NAME}"
xtrabackup --backup \
--user=${MYSQL_USER} \
--password=${MYSQL_PASS} \
--target-dir=${BACKUP_PATH} \
--incremental-basedir=${BASE_DIR} \
--parallel=${PARALLEL_THREADS} \
--compress \
--compress-threads=${COMPRESS_THREADS} \
2>>${LOG_FILE}
if [ $? -eq 0 ]; then
log "Incremental backup completed: ${BACKUP_PATH}"
cat ${BACKUP_PATH}/xtrabackup_checkpoints | tee -a ${LOG_FILE}
else
log "Incremental backup FAILED!"
exit 1
fi
}
# 清理旧备份
cleanup_old_backups() {
log "Cleaning up old backups..."
# 清理旧全量备份
find ${FULL_BACKUP_DIR} -maxdepth 1 -type d -mtime +${FULL_RETENTION_DAYS} -exec rm -rf {} \;
# 清理旧增量备份
find ${INCR_BACKUP_DIR} -maxdepth 1 -type d -mtime +${INCR_RETENTION_DAYS} -exec rm -rf {} \;
# 清理旧日志
find ${LOG_DIR} -name "*.log" -mtime +${FULL_RETENTION_DAYS} -delete
log "Cleanup completed"
}
# 主程序
case "$1" in
full)
do_full_backup
;;
incremental|incr)
do_incremental_backup
;;
*)
log "Usage: $0 "
exit 1
;;
esac
cleanup_old_backups
log "Backup script completed"✅ 定时任务配置(自动化备份关键)
用crontab设置定时任务,让脚本自动执行,不用手动操作:
# 编辑crontab(执行命令)
crontab -e
# -------------- 小型数据库(mysqldump)--------------
# 每天凌晨2点执行全备,日志写入cron.log
0 2 * * * /backup/mysql/scripts/mysqldump_backup.sh full >> /backup/mysql/logs/cron.log 2>&1
# -------------- 大型数据库(XtraBackup)--------------
# 每周日凌晨2点执行全备
0 2 * * 0 /backup/mysql/scripts/xtrabackup_backup.sh full >> /backup/mysql/logs/cron.log 2>&1
# 周一到周六凌晨2点执行增量备份
0 2 * * 1-6 /backup/mysql/scripts/xtrabackup_backup.sh incremental >> /backup/mysql/logs/cron.log 2>&1
# -------------- binlog归档(每小时一次)--------------
0 * * * * /backup/mysql/scripts/binlog_archive.sh >> /backup/mysql/logs/cron.log 2>&13.2 高频故障恢复案例(实战必备)
案例一:误删数据恢复(最常见)
场景:开发误执行DELETE语句,删除了users表中当天的数据(本想删除无效用户,忘了加WHERE条件)。
-- 误操作SQL(噩梦开始)
DELETE FROM users WHERE created_at >= '2024-01-15';
-- 正确SQL(应该加条件)
DELETE FROM users WHERE created_at >= '2024-01-15' AND status = 'inactive';恢复步骤(关键:立即记录binlog位置,避免数据覆盖):
# 第一步:立即记录当前binlog位置(重中之重,防止binlog滚动)
mysql -uroot -p -e "SHOW MASTER STATUS"
# 记录结果:File: mysql-bin.000150, Position: 123456789
# 第二步:找到最近的全量备份
ls -la /backup/mysql/full/
# 假设最新备份:all_databases_20240115_020000.sql.gz
# 第三步:解压备份,恢复到临时库(避免影响生产库)
# 解压备份
gunzip -c /backup/mysql/full/all_databases_20240115_020000.sql.gz > /tmp/restore.sql
# 创建临时库
mysql -uroot -p -e "CREATE DATABASE users_recovery"
# 只恢复users表(精准恢复,节省时间)
# 方法1:用sed提取users表
sed -n '/^-- Table structure for table `users`/,/^-- Table structure for table/p' /tmp/restore.sql > /tmp/users_table.sql
# 方法2:用awk提取(更精准)
awk '/^-- Table structure for table `users`/{f=1} f; /^UNLOCK TABLES;/{if(f) exit}' /tmp/restore.sql > /tmp/users_table.sql
# 导入到临时库
mysql -uroot -p users_recovery < /tmp/users_table.sql
# 第四步:确认备份的binlog位置(从备份文件中提取)
grep "CHANGE MASTER TO" /tmp/restore.sql | head -1
# 输出:-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000145', MASTER_LOG_POS=789012;
# 第五步:应用binlog,恢复到误删之前
# 先找到误删操作的binlog位置
mysqlbinlog /data/mysql/binlog/mysql-bin.000145 mysql-bin.000146 \
--start-position=789012 \
--database=mydb \
-v | grep -B5 -A5 "DELETE FROM.*users"
# 找到误删位置后,应用binlog到误删前
mysqlbinlog /data/mysql/binlog/mysql-bin.000145 mysql-bin.000146 \
--start-position=789012 \
--stop-position=误删操作位置 \# 替换成实际误删位置
--database=mydb | mysql -uroot -p users_recovery
# 第六步:将恢复的数据导回生产库
mysqldump -uroot -p users_recovery users | mysql -uroot -p mydb案例二:时间点恢复(PITR)
场景:数据库因误操作、病毒攻击等,需要恢复到指定时间点(比如2024-01-15 14:30:00)。
#!/bin/bash
# 文件:point_in_time_recovery.sh
# 功能:MySQL时间点恢复脚本
# 用法:直接执行,修改配置中的恢复时间和目录
RECOVERY_TIME="2024-01-15 14:30:00"# 目标恢复时间
BACKUP_DIR="/backup/mysql/full/20240115"# 最近的全量备份目录
BINLOG_DIR="/data/mysql/binlog"# binlog目录
RECOVERY_DIR="/data/mysql_recovery"# 恢复临时目录
# 第一步:准备备份(解压+应用redo log)
echo "Preparing backup..."
cp -r ${BACKUP_DIR} ${RECOVERY_DIR}
# 解压压缩的备份文件(如果是压缩备份)
cd ${RECOVERY_DIR}
for f in *.qp; do
qpress -d $f ./
done
# 准备备份,确保数据一致性
xtrabackup --prepare --target-dir=${RECOVERY_DIR}
# 第二步:获取备份的binlog位置(从备份文件中提取)
BINLOG_INFO=$(cat ${RECOVERY_DIR}/xtrabackup_binlog_info)
BINLOG_FILE=$(echo $BINLOG_INFO | awk '{print $1}')
BINLOG_POS=$(echo $BINLOG_INFO | awk '{print $2}')
echo "Backup binlog position: ${BINLOG_FILE}:${BINLOG_POS}"
# 第三步:找到需要应用的binlog文件
BINLOG_FILES=$(ls ${BINLOG_DIR}/mysql-bin.* | sort | awk -v start="${BINLOG_FILE}" '$0 >= start')
# 第四步:应用binlog到指定时间点
echo "Applying binlog until ${RECOVERY_TIME}..."
mysqlbinlog ${BINLOG_FILES} \
--start-position=${BINLOG_POS} \
--stop-datetime="${RECOVERY_TIME}" \
> ${RECOVERY_DIR}/binlog_recovery.sql
# 第五步:恢复数据到生产环境
echo "Stopping MySQL..."
systemctl stop mysqld
# 备份当前生产数据目录(安全起见,防止恢复失败)
mv /data/mysql/data /data/mysql/data_old_$(date +%Y%m%d_%H%M%S)
# 恢复备份
xtrabackup --copy-back --target-dir=${RECOVERY_DIR}
chown -R mysql:mysql /data/mysql/data# 设置权限
# 启动MySQL
systemctl start mysqld
# 应用binlog增量,完成时间点恢复
echo "Applying binlog recovery..."
mysql -uroot -p < ${RECOVERY_DIR}/binlog_recovery.sql
echo "Point-in-time recovery completed to ${RECOVERY_TIME}"案例三:大表备份与恢复(500GB+)
场景:orders表有5亿条数据,单表500GB,直接备份/恢复会卡死,用分区导出导入,提升效率。
# 备份脚本:large_table_backup.sh(分区导出)#!/bin/bashTABLE="orders"DB="ecommerce"BACKUP_DIR="/backup/mysql/large_tables"DATE=$(date +%Y%m%d)PARALLEL_JOBS=4# 4线程并行,按需调整mkdir -p ${BACKUP_DIR}/${DATE}# 获取表的主键范围(按主键分区)MAX_ID=$(mysql -ubackup -p'BackupPass@2024' -N -e "SELECT MAX(id) FROM ${DB}.${TABLE}")BATCH_SIZE=$((MAX_ID / PARALLEL_JOBS))# 并行导出(按主键分段,避免单线程卡死)for i in $(seq 1 ${PARALLEL_JOBS}); do START_ID=$(( (i-1) * BATCH_SIZE + 1 )) END_ID=$(( i * BATCH_SIZE )) if [ $i -eq ${PARALLEL_JOBS} ]; then END_ID=${MAX_ID}# 最后一段包含剩余数据 fi echo "Exporting ${TABLE} rows ${START_ID} to ${END_ID}..." mysqldump -ubackup -p'BackupPass@2024' \ --single-transaction \ --no-create-info \# 只导出数据,表结构单独导出 --where="id >= ${START_ID} AND id
页:
[1]