mysql数据库完整备份恢复方案(二)

一、备份脚本

(一)备份脚本

创建了个mysql_backup.sh文件。

bash 复制代码
#!/bin/bash
# MySQL备份脚本(按日期分文件夹管理)
# 功能:备份指定数据库,排除iplatform库的特定日志表,自动压缩并清理旧备份

#######################################
# 【配置参数区】-- 在此处修改以下参数
#######################################
DB_USER="root"                   # 数据库用户名
DB_PASS="123456"            # 数据库密码(在此处修改)
BACKUP_ROOT_DIR="/home/backup"   # 备份根目录(所有日期的备份放在这里)
DB_LIST=(                        # 需要备份的数据库列表(在此处增删)
  "user"
  "order"
)
EXCLUDE_TABLES=(                 # 需要排除的表(格式:库名.表名)
  "user.log"
  "order.log"
)
RETENTION_DAYS=30                # 备份保留天数(默认30天)
export PATH=$PATH:/usr/local/mysql/bin    # 设置环境变量
#######################################
# 【配置结束】-- 以下内容无需修改
#######################################

# 初始化变量(按日期创建子文件夹)
CURRENT_DATE=$(date +%Y%m%d)      # 当天日期(用于文件夹命名)
TIMESTAMP=$(date +%Y%m%d_%H%M%S)  # 完整时间戳(用于文件名)
BACKUP_DIR="$BACKUP_ROOT_DIR/$CURRENT_DATE"  # 当天备份的具体目录
LOG_FILE="$BACKUP_DIR/backup_$TIMESTAMP.log"

# 检查并创建备份目录(包括当天子文件夹)
if [ ! -d "$BACKUP_DIR" ]; then
  echo "备份目录 $BACKUP_DIR 不存在,尝试创建..."
  mkdir -p "$BACKUP_DIR"
  # 检查目录创建结果
  if [ $? -ne 0 ]; then
    echo "错误:无法创建备份目录 $BACKUP_DIR,请检查权限!"
    exit 1
  fi
  echo "备份目录创建成功"
fi

# 写入日志头部
echo "===== 备份开始于 $TIMESTAMP =====" >> "$LOG_FILE"
echo "备份数据库列表:${DB_LIST[*]}" >> "$LOG_FILE"
echo "排除表列表:${EXCLUDE_TABLES[*]}" >> "$LOG_FILE"
echo "备份文件存放目录:$BACKUP_DIR" >> "$LOG_FILE"

# 备份单个数据库的函数
backup_database() {
  local db_name=$1
  local exclude_params=""
  
  # 生成排除表参数(仅对iplatform库生效)
  if [ "$db_name" = "iplatform" ]; then
    for table in "${EXCLUDE_TABLES[@]}"; do
      exclude_params+=" --ignore-table=$table"
    done
  fi
  
  # 执行备份并压缩(文件存放在当天子文件夹中)
  echo "开始备份数据库:$db_name" >> "$LOG_FILE"
  mysqldump -u"$DB_USER" -p"$DB_PASS" --databases "$db_name" $exclude_params | gzip > "$BACKUP_DIR/${db_name}_$TIMESTAMP.sql.gz"
  
  # 检查备份结果
  if [ $? -eq 0 ]; then
    echo " 数据库 $db_name 备份成功:${db_name}_$TIMESTAMP.sql.gz" >> "$LOG_FILE"
    echo " 数据库 $db_name 备份成功"  # 同时输出到控制台
  else
    echo " 数据库 $db_name 备份失败!" >> "$LOG_FILE"
    echo " 数据库 $db_name 备份失败!"  # 同时输出到控制台
  fi
}

# 批量备份所有数据库
for db in "${DB_LIST[@]}"; do
  backup_database "$db"
done

# 清理过期备份(删除整个过期日期的文件夹)
echo "开始清理${RETENTION_DAYS}天前的备份文件夹..." >> "$LOG_FILE"
find "$BACKUP_ROOT_DIR" -maxdepth 1 -type d -name "20[0-9][0-9][0-1][0-9][0-3][0-9]" -mtime +"$RETENTION_DAYS" -exec rm -rf {} \;
echo "清理完成" >> "$LOG_FILE"

# 写入日志尾部
echo "===== 备份结束于 $(date +%Y%m%d_%H%M%S) =====" >> "$LOG_FILE"
echo "" >> "$LOG_FILE"

echo "所有备份操作已完成,详细日志:$LOG_FILE"

(二)远程上传

数据库光完成备份了还不行,放到本地服务器也不可靠,一般还得将备份文件上传到异地服务器进行保存,以防止本地数据库服务器出现问题。

1.前置准备工作

配置SSH免密登录

bash 复制代码
# 在备份服务器生成密钥
ssh-keygen -t rsa -b 4096

一路回车选默认值

bash 复制代码
# 复制公钥到远程服务器
ssh-copy-id -p 22 backup_user@192.168.1.100

选yes然后输入远程服务器密码

bash 复制代码
# 测试连接
ssh -p 22 backup_user@192.168.1.100 "echo '连接成功'"
bash 复制代码
# 创建必要的目录
mkdir -p /backup
chmod 755 /backup

安装rsync(如果未安装)

bash 复制代码
# CentOS/RHEL
yum install rsync -y

# Ubuntu/Debian
apt-get install rsync -y

2.上传脚本

bash 复制代码
#!/bin/bash
# 备份文件上传到远程服务器脚本
# 配合mysql_backup.sh使用,建议在备份完成后执行

#######################################
# 【配置参数区】-- 在此处修改以下参数
#######################################
BACKUP_ROOT_DIR="/home/backup"      # 本地备份根目录(与备份脚本相同)
REMOTE_USER="backup_user"           # 远程服务器用户名
REMOTE_HOST="192.168.1.100"         # 远程服务器IP或域名
REMOTE_PORT="22"                    # SSH端口(默认22)
REMOTE_BACKUP_DIR="/data/backup"    # 远程服务器备份目录
RETENTION_DAYS=30                   # 远程保留天数(与本地一致)
LOG_DIR="/var/log/backup"           # 上传日志目录
RSYNC_OPTIONS="-avz --delete"       # rsync参数:归档、压缩、删除远程多余文件
#######################################
# 【配置结束】-- 以下内容无需修改
#######################################

# 初始化变量
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
UPLOAD_LOG="$LOG_DIR/upload_$TIMESTAMP.log"
CURRENT_DATE=$(date +%Y%m%d)
LOCAL_TODAY_DIR="$BACKUP_ROOT_DIR/$CURRENT_DATE"

# 检查日志目录
mkdir -p "$LOG_DIR"

# 记录开始时间
echo "===== 备份上传开始于 $(date +'%Y-%m-%d %H:%M:%S') =====" > "$UPLOAD_LOG"

# 检查本地今日备份目录是否存在
if [ ! -d "$LOCAL_TODAY_DIR" ]; then
    echo "错误:今日备份目录不存在 $LOCAL_TODAY_DIR" | tee -a "$UPLOAD_LOG"
    echo "请先运行备份脚本生成备份文件" | tee -a "$UPLOAD_LOG"
    exit 1
fi

# 检查本地是否有备份文件
BACKUP_FILES_COUNT=$(find "$LOCAL_TODAY_DIR" -name "*.gz" -type f | wc -l)
if [ "$BACKUP_FILES_COUNT" -eq 0 ]; then
    echo "警告:今日备份目录中没有备份文件" | tee -a "$UPLOAD_LOG"
    echo "目录:$LOCAL_TODAY_DIR" | tee -a "$UPLOAD_LOG"
fi

# 方法1:使用rsync(推荐,支持断点续传和增量同步)
echo "开始使用rsync同步备份文件到远程服务器..." | tee -a "$UPLOAD_LOG"
echo "远程服务器:$REMOTE_USER@$REMOTE_HOST:$REMOTE_BACKUP_DIR" | tee -a "$UPLOAD_LOG"

rsync $RSYNC_OPTIONS -e "ssh -p $REMOTE_PORT" \
    "$BACKUP_ROOT_DIR/" \
    "$REMOTE_USER@$REMOTE_HOST:$REMOTE_BACKUP_DIR/" \
    2>&1 | tee -a "$UPLOAD_LOG"

RSYNC_EXIT_CODE=${PIPESTATUS[0]}

if [ $RSYNC_EXIT_CODE -eq 0 ]; then
    echo "rsync同步成功" | tee -a "$UPLOAD_LOG"
    
    # 清理远程服务器过期备份
    echo "开始清理远程服务器过期备份..." | tee -a "$UPLOAD_LOG"
    ssh -p "$REMOTE_PORT" "$REMOTE_USER@$REMOTE_HOST" \
        "find \"$REMOTE_BACKUP_DIR\" -maxdepth 1 -type d -name '20[0-9][0-9][0-1][0-9][0-3][0-9]' -mtime +$RETENTION_DAYS -exec rm -rf {} \;" \
        2>&1 | tee -a "$UPLOAD_LOG"
else
    echo "警告:rsync同步部分文件可能失败,退出码: $RSYNC_EXIT_CODE" | tee -a "$UPLOAD_LOG"
    echo "可能是网络问题或权限问题" | tee -a "$UPLOAD_LOG"
fi

# 方法2:使用scp(备用方案,每次传输整个文件)
# 如果需要使用scp,可以取消下面代码的注释
# echo "开始使用scp上传今日备份..." | tee -a "$UPLOAD_LOG"
# scp -P "$REMOTE_PORT" -r "$LOCAL_TODAY_DIR" \
#     "$REMOTE_USER@$REMOTE_HOST:$REMOTE_BACKUP_DIR/" \
#     2>&1 | tee -a "$UPLOAD_LOG"

# 记录结束时间
echo "===== 备份上传结束于 $(date +'%Y-%m-%d %H:%M:%S') =====" >> "$UPLOAD_LOG"

# 输出总结
echo "" | tee -a "$UPLOAD_LOG"
echo "上传完成总结:" | tee -a "$UPLOAD_LOG"


echo "- 本地备份目录: $BACKUP_ROOT_DIR" | tee -a "$UPLOAD_LOG"
echo "- 远程备份目录: $REMOTE_BACKUP_DIR" | tee -a "$UPLOAD_LOG"
echo "- 上传日志文件: $UPLOAD_LOG" | tee -a "$UPLOAD_LOG"
echo "- 今日备份文件数: $BACKUP_FILES_COUNT" | tee -a "$UPLOAD_LOG"

exit $RSYNC_EXIT_CODE

3.定时任务

bash 复制代码
# 每天02:00执行备份,02:30执行上传
0 2 * * * /path/to/mysql_backup.sh >> /var/log/backup/cron_backup.log 2>&1
30 2 * * * /path/to/upload_backup.sh >> /var/log/backup/cron_upload.log 2>&1

二、备份恢复

如何恢复数据取决于是怎么备份的。可能需要以下部分或全部步骤。

  • 停止 MySQL 服务器。
  • 记录服务器的配置和文件权限。
  • 将数据从备份中移到 MySQL数据目录
  • 改变配置
  • 改变文件权限
  • 以限制访问模式重启服务器,等待完成启动。
  • 载入逻辑备份文件
  • 检查和重放二进制日志
  • 检测已经还原的数据
  • 以完全权限重启服务器

(一)恢复物理备份

物理恢复的核心是文件替换/导入,但需匹配存储引擎特性,否则易导致数据不一致或服务启动失败。

1.MyISAM表的物理恢复

MyISAM表文件(.frm表结构、.MYD数据、.MYI索引)相对独立,但恢复时需保证表无读写操作。

恢复步骤:

  • 锁表并刷盘:在目标服务器执行
sql 复制代码
LOCK TABLES `目标表` READ;  -- 禁止表写入
FLUSH TABLES `目标表`;      -- 将表数据刷入磁盘
  • 替换表文件 :将备份的.frm/.MYD/.MYI文件复制到目标库的数据目录(如/var/lib/mysql/数据库名/)。
  • 解锁表
sql 复制代码
UNLOCK TABLES;

关键注意事项:

  • 若直接在服务器运行时复制文件(未锁表),可能导致.MYD/.MYI文件写入不完整,恢复后表数据损坏。
  • 恢复后需执行CHECK TABLE 目标表;验证表完整性。

2.InnoDB共享表空间(单文件)恢复

若InnoDB使用共享表空间 (所有表存于ibdata1),恢复需保证文件完整性与一致性。

恢复步骤"

  • 关闭MySQL服务:InnoDB共享表空间无法在服务运行时恢复,需执行
bash 复制代码
   systemctl stop mysql
  • 替换核心文件 :将备份的ibdata1(共享表空间)、ib_logfile*(事务日志)、数据库名/下的.frm文件,完整替换到目标服务器的数据目录。
    必须同时替换ib_logfile*:InnoDB启动时会校验日志与表空间的LSN(日志序列号),若不匹配则拒绝启动。
  • 修正文件权限 :确保文件归属为mysql用户
bash 复制代码
  chown -R mysql:mysql /var/lib/mysql
  • 启动MySQL并验证 :启动服务后执行CHECK TABLES IN 数据库名;,确认表完整性。

3.InnoDB单表空间(file-per-table)恢复

若启用innodb_file_per_table(每张表对应独立.ibd文件),可恢复单表,但需通过InnoDB的表空间导入/导出机制(原始内容中"只能在原服务器还原"的限制,在MySQL 5.6+中可跨服务器,但需保证表结构一致):

恢复步骤

  • 在目标服务器创建空表 :先在目标库中创建与备份表结构完全一致的表(可通过逻辑备份的CREATE TABLE语句生成)。
  • 丢弃空表的表空间
sql 复制代码
   ALTER TABLE `目标表` DISCARD TABLESPACE;

执行后目标库中会删除该表的.ibd文件。

  • 复制备份的.ibd文件 :将备份的目标表.ibd复制到目标库的数据目录,并修正权限:
bash 复制代码
   chown mysql:mysql /var/lib/mysql/数据库名/目标表.ibd
  • 导入表空间
sql 复制代码
   ALTER TABLE `目标表` IMPORT TABLESPACE;
  • 验证表完整性
sql 复制代码
   CHECK TABLE `目标表`;

关键限制:

  • 目标表的结构需与备份表完全一致(字段、类型、索引);
  • 若跨服务器恢复,需保证两端InnoDB配置一致(如innodb_page_size),否则表空间ID不匹配会导致导入失败。

4.物理恢复后的启动前检查:

权限与SELinux检查

  • chown mysql:mysql外,若服务器启用SELinux,需修复文件上下文:restorecon -Rv /var/lib/mysql

  • 若权限正确但服务仍无法启动,可临时关闭SELinux验证(生产环境需通过setsebool配置而非永久关闭)。
    配置文件匹配 :确保目标服务器的my.cnf(如innodb_buffer_pool_sizeinnodb_log_file_size)与备份源服务器一致,否则InnoDB可能因参数不匹配启动失败。

  • 启动后的错误日志监测

    启动服务后实时查看错误日志,定位潜在问题:tail -f /var/log/mysql/error.log

    • 若InnoDB提示"LSN不匹配",需重新替换完整的ibdata1ib_logfile*
    • 若提示"表不存在",需检查.frm文件是否已正确复制。

(二)逻辑备份恢复

逻辑恢复是通过MySQL执行SQL/文本文件加载数据,需关注效率、错误处理、资源占用

1.SQL文件的常规加载

基础操作:

直接通过管道加载(推荐,避免临时文件):

bash 复制代码
# 未压缩文件
mysql -u root -p < 备份文件.sql

# 压缩文件(直接解压+加载,无需单独解压)
gunzip -c 备份文件.sql.gz | mysql -u root -p  # gzip压缩
xz -d -c 备份文件.sql.xz | mysql -u root -p    # xz压缩

通过SOURCE命令加载(适合远程连接场景):

sql 复制代码
SET SQL_LOG_BIN = 0;  -- 关闭二进制日志,避免恢复操作同步到备库
SOURCE /路径/备份文件.sql;
SET SQL_LOG_BIN = 1;

2.单表恢复(避免"暴力抽取")

若用mydumper导出,默认会按表生成独立文件(如数据库名.表名.sql),恢复时直接加载对应表的文件即可:

bash 复制代码
mysql -u root -p 数据库名 < 数据库名.目标表.sql

若只有全库SQL文件,可通过以下方式抽取单表(比sed更易懂):

抽取CREATE TABLE语句:

bash 复制代码
     grep -n "CREATE TABLE `目标表`" 备份文件.sql  # 找到行号
     head -n 行号+10 备份文件.sql | tail -n 20  # 截取表结构(依表结构长度调整)

抽取INSERT语句:

bash 复制代码
     grep "INSERT INTO `目标表`" 备份文件.sql > 目标表_insert.sql

先执行表结构SQL,再加载INSERT文件。

3.恢复中的错误处理

SOURCE命令的错误续跑:默认情况下,SOURCE遇到错误会停止,可通过以下方式忽略错误(仅适用于非关键表):

sql 复制代码
  SET sql_mode = 'NO_ENGINE_SUBSTITUTION';
  SOURCE /路径/备份文件.sql --force;  # 客户端连接时加--force参数

压缩文件的命名管道加载:若需用SOURCE加载压缩文件,可通过命名管道实现(避免解压):

bash 复制代码
mkfifo backup_pipe  # 创建命名管道
gunzip -c 备份文件.sql.gz > backup_pipe &  # 后台解压到管道
mysql -u root -p -e "SOURCE backup_pipe;"  # 从管道加载
rm backup_pipe

五、实操建议

1.关键风险规避

  • InnoDB 物理备份:禁止直接复制运行中的 .ibd 文件,需使用 XtraBackup 等工具生成一致性备份;恢复后必须执行 innochecksum(文件校验)+ CHECK TABLES(数据校验)。
  • MyISAM 物理备份:可直接复制 .MYD/.MYI 文件,但需先执行 FLUSH TABLES WITH READ LOCK,避免备份期间数据写入导致文件损坏。
  • 跨版本还原:物理备份仅建议在同大版本间还原(如 8.0.20→8.0.30),跨大版本(5.7→8.0)需先通过逻辑备份迁移,避免表空间格式不兼容。

2.实操流程示例

bash 复制代码
# 1. 每周日执行全量物理备份(XtraBackup)
xtrabackup --user=root --backup --target-dir=/backup/full/$(date +%Y%m%d) --compress

# 2. 每日执行增量物理备份(基于前一次全量/增量备份)
xtrabackup --user=root --backup --target-dir=/backup/inc/$(date +%Y%m%d) --incremental-basedir=/backup/full/20240512 --compress

# 3. 每月1日将物理备份还原到备用服务器
xtrabackup --prepare --target-dir=/backup/full/20240512
cp -r /backup/full/20240512 /data/mysql
chown -R mysql:mysql /data/mysql
systemctl start mysql

# 4. 备用服务器执行逻辑备份(归档用)
mysqldump --user=root --all-databases --single-transaction --quick --compress --output=/backup/logical/20240501_all.sql.gz

# 5. 验证逻辑备份有效性
mysql --user=root -e "SOURCE /backup/logical/20240501_all.sql.gz" test_db
mysqlcheck -u root -p --all-databases
相关推荐
l1t2 小时前
DeepSeek总结的Turso的CTE支持情况
数据库·sqlite·turso
重生之绝世牛码2 小时前
Linux软件安装 —— ClickHouse集群安装(集成Zookeeper)+ chproxy负载均衡
大数据·linux·数据库·clickhouse·软件安装·clickhouse集群安装·clickhouse负载均衡
l1t2 小时前
修改德哥的PostgreSQL求解数独SQL在cedardb上运行
数据库·sql·postgresql·cedardb
想做一只开心的菜鸡2 小时前
DARTS#01 | Tournament Sort算法 | MySQL深度翻页优化技巧 | 论文ByteSlice Review
数据库·mysql·算法
瑶山2 小时前
Spring Cloud微服务搭建二、分布式定时任务Quartz+MySQL接入
分布式·mysql·spring cloud·微服务·quartz
一只自律的鸡2 小时前
【MySQL】第八章 数据类型
数据库·mysql
计算机学姐2 小时前
基于SpringBoot的校园跑腿系统【数据可视化统计+原创精品】
java·vue.js·spring boot·后端·mysql·信息可视化·echarts
算法小菜鸟成长心得3 小时前
postgresql18 版本,使用navicate15版本出现不兼容问题
数据库
while(1){yan}13 小时前
Spring事务
java·数据库·spring boot·后端·java-ee·mybatis