定时任务 crontab

  • -e : 编辑定时任务
  • -l : 显示定时任务
    `sh

[root@slave01 ~]# crontab -l

* * * * * shell command parameter

          • echo “hello world”
            #分 时 日 月 周 命令
            0 2 * * * /bin/ntpdate ntp.ntsc.ac.cn #定时做时间同步
            */15 2 1 * * echo “hello”
            */2 * * * * mysqladmin ping localhost
            1-15,45-50 2 * * * echo “1” >/opt/cronta.log
            30-50/10 * * * * echo 11
            0 2 * * 1-6 /backup/bin/mysqlbackup_increment
            0 2 * * 0 /backup/bin/mysqlbackup_whole
            1 * * * * /backup/bin/rotatelog.sh >> /backup/log/rotate.log 2>&1
            1 * * * * sleep 30; /backup/bin/rotatelog.sh >> /backup/log/rotate.log 2>&1


# 数据不同步,在gtid模式下

在master机器上导出数据,记录当前的position

mysqldump -uroot -pRoot@1234 –single-transaction –lock-tables –all-databases –triggers –routines –events >all.dat

scp all.dat user@remote_ip:.


必须在slave机器先执行 
`mysql -uroot -pRoot@1234 -e "RESET MASTER;"`
然后再导入 master机器上的数据库。

`mysql -uroot -pRoot@1234 < all.dat 


# MySQL 备份工具详解与备份脚本

数据库一般都需要进行备份,备份的策略一般是

- 周一-周六 每天增量备份
- 周日 全量备份
- 备份保留最近的7份文件
- 备份的时间一般是凌晨 2-3## 一、mysqldump 详解

### 1.1 mysqldump 基本用法

数据量小50G,比较合适
另外一个公司 mysqldumper,适合50-200G大小的数据库
xtra-backup, 200G都适合。

```bash
# 基本语法
mysqldump [options] database [tables]

# 常用参数
-u, --user=username      # 用户名
-p, --password[=password] # 密码
-h, --host=hostname      # 主机名
-P, --port=port          # 端口
--databases              # 备份多个数据库
--all-databases          # 备份所有数据库
--single-transaction     # 对InnoDB使用事务保证一致性
--master-data[=value]    # 记录二进制日志位置(1-注释,2-变更)
--flush-logs             # 备份前刷新日志
--routines               # 包含存储过程和函数
--events                 # 包含事件
--triggers               # 包含触发器
--lock-tables           # 对所有表加读锁
--skip-lock-tables      # 不加锁
--compress              # 压缩传输
--where='where_condition' # 条件备份

1.2 mysqldump 全量备份示例

# 备份单个数据库
mysqldump -u root -p --single-transaction --routines --tri·ggers mydb > backup.sql

# 备份多个数据库
mysqldump -u root -p --databases db1 db2 db3 > backup.sql

# 备份所有数据库
mysqldump -u root -p --all-databases --single-transaction > alldb_backup.sql

# 备份文件带时间的数据库
mysqldump -uroot -pRoot@1234 --single-transaction  --lock-tables  db03   >/backup/db03.`date +%Y%m%d-%H%M%S`.sql

# 压缩备份
mysqldump -u root -p mydb | gzip > backup.sql.gz

# 分表备份
mysqldump -u root -p mydb table1 table2 > tables_backup.sql

二、Percona XtraBackup 详解

2.1 XtraBackup 特性

  • 物理备份,速度快
  • 支持热备份(备份期间不锁表)
  • 支持增量备份
  • 支持压缩备份
  • 支持流式备份

2.2 常用命令

# 全量备份
xtrabackup --backup --target-dir=/path/to/backup --user=root --password=xxx

# 增量备份(基于前一个备份)
xtrabackup --backup --target-dir=/path/to/incremental \
  --incremental-basedir=/path/to/full_backup \
  --user=root --password=xxx

# 准备备份(应用事务日志)
xtrabackup --prepare --apply-log-only --target-dir=/path/to/full_backup
xtrabackup --prepare --target-dir=/path/to/full_backup

# 恢复备份
xtrabackup --copy-back --target-dir=/path/to/backup

三、备份脚本实现

3.1 mysqldump 备份脚本

全量备份脚本 (mysqldump_full.sh)

#!/bin/bash
# mysqldump_full.sh - 全量备份脚本

# 配置参数
BACKUP_DIR="/data/backup/mysql"
LOG_FILE="/var/log/mysql_backup.log"
MYSQL_USER="root"
MYSQL_PASSWORD="your_password"
MYSQL_HOST="localhost"
MYSQL_PORT="3306"
RETENTION_DAYS=30
COMPRESS_LEVEL=9

# 创建备份目录
DATE=$(date +%Y%m%d_%H%M%S)
FULL_BACKUP_DIR="${BACKUP_DIR}/full_${DATE}"
mkdir -p ${FULL_BACKUP_DIR}

# 记录开始时间
echo "==========================================" >> ${LOG_FILE}
echo "全量备份开始: $(date)" >> ${LOG_FILE}

# 备份所有数据库
echo "备份所有数据库..." >> ${LOG_FILE}
mysqldump \
  --user=${MYSQL_USER} \
  --password=${MYSQL_PASSWORD} \
  --host=${MYSQL_HOST} \
  --port=${MYSQL_PORT} \
  --all-databases \
  --single-transaction \
  --routines \
  --events \
  --triggers \
  --master-data=2 \
  --flush-logs \
  --default-character-set=utf8mb4 > ${FULL_BACKUP_DIR}/full_backup_${DATE}.sql 2>> ${LOG_FILE}

# 检查备份结果
if [ $? -eq 0 ]; then
    echo "数据库备份成功" >> ${LOG_FILE}

    # 压缩备份文件
    echo "压缩备份文件..." >> ${LOG_FILE}
    gzip -${COMPRESS_LEVEL} ${FULL_BACKUP_DIR}/full_backup_${DATE}.sql

    # 备份配置文件
    echo "备份MySQL配置文件..." >> ${LOG_FILE}
    cp /etc/my.cnf ${FULL_BACKUP_DIR}/ 2>> ${LOG_FILE} || cp /etc/mysql/my.cnf ${FULL_BACKUP_DIR}/ 2>> ${LOG_FILE}

    # 生成备份信息
    echo "备份时间: ${DATE}" > ${FULL_BACKUP_DIR}/backup.info
    echo "备份类型: 全量备份" >> ${FULL_BACKUP_DIR}/backup.info
    echo "备份大小: $(du -sh ${FULL_BACKUP_DIR} | awk '{print $1}')" >> ${FULL_BACKUP_DIR}/backup.info

else
    echo "数据库备份失败" >> ${LOG_FILE}
    rm -rf ${FULL_BACKUP_DIR}
    exit 1
fi

# 清理旧备份
echo "清理超过${RETENTION_DAYS}天的旧备份..." >> ${LOG_FILE}
find ${BACKUP_DIR}/full_* -type d -mtime +${RETENTION_DAYS} -exec rm -rf {} \; 2>> ${LOG_FILE}

echo "全量备份完成: $(date)" >> ${LOG_FILE}
echo "备份目录: ${FULL_BACKUP_DIR}" >> ${LOG_FILE}
echo "==========================================" >> ${LOG_FILE}

增量备份脚本 (mysqldump_incremental.sh)

#!/bin/bash
# mysqldump_incremental.sh - 增量备份脚本(基于二进制日志)

# 配置参数
BACKUP_DIR="/data/backup/mysql"
LOG_FILE="/var/log/mysql_backup.log"
MYSQL_USER="root"
MYSQL_PASSWORD="your_password"
MYSQL_HOST="localhost"
MYSQL_PORT="3306"
RETENTION_DAYS=7
BINLOG_DIR="/var/lib/mysql"

# 获取当前二进制日志信息
DATE=$(date +%Y%m%d_%H%M%S)
INCR_BACKUP_DIR="${BACKUP_DIR}/incr_${DATE}"
mkdir -p ${INCR_BACKUP_DIR}

# 记录开始时间
echo "==========================================" >> ${LOG_FILE}
echo "增量备份开始: $(date)" >> ${LOG_FILE}

# 刷新日志,生成新的二进制日志文件
mysql -u${MYSQL_USER} -p${MYSQL_PASSWORD} -h${MYSQL_HOST} -P${MYSQL_PORT} \
  -e "FLUSH BINARY LOGS;" 2>> ${LOG_FILE}

# 获取当前二进制日志文件
CURRENT_BINLOG=$(mysql -u${MYSQL_USER} -p${MYSQL_PASSWORD} -h${MYSQL_HOST} -P${MYSQL_PORT} \
  -N -e "SHOW MASTER STATUS\G" 2>> ${LOG_FILE} | grep 'File:' | awk '{print $2}')
CURRENT_POSITION=$(mysql -u${MYSQL_USER} -p${MYSQL_PASSWORD} -h${MYSQL_HOST} -P${MYSQL_PORT} \
  -N -e "SHOW MASTER STATUS\G" 2>> ${LOG_FILE} | grep 'Position:' | awk '{print $2}')

echo "当前二进制日志: ${CURRENT_BINLOG}" >> ${LOG_FILE}
echo "当前位置: ${CURRENT_POSITION}" >> ${LOG_FILE}

# 查找上次全量备份的信息
LAST_FULL_BACKUP=$(find ${BACKUP_DIR} -name "full_backup_*.sql.gz" -type f | sort | tail -n1)
if [ -z "${LAST_FULL_BACKUP}" ]; then
    echo "未找到全量备份,请先执行全量备份" >> ${LOG_FILE}
    exit 1
fi

# 获取上次备份的二进制日志位置
LAST_BINLOG_INFO=$(zgrep "CHANGE MASTER TO" ${LAST_FULL_BACKUP} | head -1)
if [ -z "${LAST_BINLOG_INFO}" ]; then
    echo "无法从全量备份中获取二进制日志信息" >> ${LOG_FILE}
    exit 1
fi

# 从上次全量备份后,复制所有二进制日志
echo "备份二进制日志..." >> ${LOG_FILE}
cp ${BINLOG_DIR}/mysql-bin.* ${INCR_BACKUP_DIR}/ 2>> ${LOG_FILE}

# 生成备份信息
echo "备份时间: ${DATE}" > ${INCR_BACKUP_DIR}/backup.info
echo "备份类型: 增量备份" >> ${INCR_BACKUP_DIR}/backup.info
echo "基于全量备份: ${LAST_FULL_BACKUP}" >> ${INCR_BACKUP_DIR}/backup.info
echo "二进制日志范围: 从上次全量备份后 到 ${CURRENT_BINLOG}" >> ${INCR_BACKUP_DIR}/backup.info
echo "当前位置: ${CURRENT_POSITION}" >> ${INCR_BACKUP_DIR}/backup.info

# 清理旧增量备份
echo "清理超过${RETENTION_DAYS}天的旧增量备份..." >> ${LOG_FILE}
find ${BACKUP_DIR}/incr_* -type d -mtime +${RETENTION_DAYS} -exec rm -rf {} \; 2>> ${LOG_FILE}

echo "增量备份完成: $(date)" >> ${LOG_FILE}
echo "备份目录: ${INCR_BACKUP_DIR}" >> ${LOG_FILE}
echo "==========================================" >> ${LOG_FILE}

3.2 XtraBackup 备份脚本

全量备份脚本 (xtrabackup_full.sh)

#!/bin/bash
# xtrabackup_full.sh - XtraBackup全量备份脚本

# 配置参数
BACKUP_BASE="/data/backup/xtrabackup"
LOG_FILE="/var/log/xtrabackup.log"
MYSQL_USER="root"
MYSQL_PASSWORD="your_password"
MYSQL_DATA_DIR="/var/lib/mysql"
RETENTION_DAYS=30
THREADS=4  # 并行线程数

# 创建备份目录
DATE=$(date +%Y%m%d_%H%M%S)
FULL_BACKUP_DIR="${BACKUP_BASE}/full_${DATE}"
mkdir -p ${FULL_BACKUP_DIR}

# 记录开始时间
echo "==========================================" >> ${LOG_FILE}
echo "XtraBackup全量备份开始: $(date)" >> ${LOG_FILE}
echo "备份目录: ${FULL_BACKUP_DIR}" >> ${LOG_FILE}

# 执行全量备份
echo "执行全量备份..." >> ${LOG_FILE}
xtrabackup \
  --backup \
  --target-dir=${FULL_BACKUP_DIR} \
  --user=${MYSQL_USER} \
  --password=${MYSQL_PASSWORD} \
  --parallel=${THREADS} \
  --compress \
  --compress-threads=${THREADS} \
  --stream=xbstream \
  --extra-lsndir=${FULL_BACKUP_DIR} \
  2>> ${LOG_FILE} | gzip > ${FULL_BACKUP_DIR}/backup.xbstream.gz

# 检查备份结果
if [ $? -eq 0 ]; then
    echo "全量备份成功" >> ${LOG_FILE}

    # 生成备份信息
    echo "备份时间: ${DATE}" > ${FULL_BACKUP_DIR}/backup.info
    echo "备份类型: 全量备份" >> ${FULL_BACKUP_DIR}/backup.info
    echo "LSN信息:" >> ${FULL_BACKUP_DIR}/backup.info
    cat ${FULL_BACKUP_DIR}/xtrabackup_checkpoints >> ${FULL_BACKUP_DIR}/backup.info

    # 准备备份(测试)
    echo "准备备份(应用日志)..." >> ${LOG_FILE}
    xtrabackup --prepare --target-dir=${FULL_BACKUP_DIR} 2>> ${LOG_FILE}

    if [ $? -eq 0 ]; then
        echo "备份准备成功" >> ${LOG_FILE}
    else
        echo "备份准备失败" >> ${LOG_FILE}
    fi

else
    echo "全量备份失败" >> ${LOG_FILE}
    rm -rf ${FULL_BACKUP_DIR}
    exit 1
fi

# 清理旧备份
echo "清理超过${RETENTION_DAYS}天的旧备份..." >> ${LOG_FILE}
find ${BACKUP_BASE}/full_* -type d -mtime +${RETENTION_DAYS} -exec rm -rf {} \; 2>> ${LOG_FILE}

echo "全量备份完成: $(date)" >> ${LOG_FILE}
echo "备份大小: $(du -sh ${FULL_BACKUP_DIR} | awk '{print $1}')" >> ${LOG_FILE}
echo "==========================================" >> ${LOG_FILE}

增量备份脚本 (xtrabackup_incremental.sh)

#!/bin/bash
# xtrabackup_incremental.sh - XtraBackup增量备份脚本

# 配置参数
BACKUP_BASE="/data/backup/xtrabackup"
LOG_FILE="/var/log/xtrabackup.log"
MYSQL_USER="root"
MYSQL_PASSWORD="your_password"
RETENTION_DAYS=7
THREADS=4

# 查找最新的全量备份
DATE=$(date +%Y%m%d_%H%M%S)
LATEST_FULL=$(find ${BACKUP_BASE} -name "full_*" -type d | sort | tail -n1)

if [ -z "${LATEST_FULL}" ]; then
    echo "未找到全量备份,请先执行全量备份" >> ${LOG_FILE}
    exit 1
fi

# 查找最新的增量备份
LATEST_INCR=$(find ${BACKUP_BASE} -name "incr_*" -type d | sort | tail -n1)

# 确定增量基准目录
if [ -z "${LATEST_INCR}" ]; then
    # 如果没有增量备份,基于最新的全量备份
    INCREMENTAL_BASEDIR=${LATEST_FULL}
    echo "基于全量备份: ${LATEST_FULL}" >> ${LOG_FILE}
else
    # 如果有增量备份,基于最新的增量备份
    INCREMENTAL_BASEDIR=${LATEST_INCR}
    echo "基于增量备份: ${LATEST_INCR}" >> ${LOG_FILE}
fi

# 创建增量备份目录
INCR_BACKUP_DIR="${BACKUP_BASE}/incr_${DATE}"
mkdir -p ${INCR_BACKUP_DIR}

# 记录开始时间
echo "==========================================" >> ${LOG_FILE}
echo "XtraBackup增量备份开始: $(date)" >> ${LOG_FILE}
echo "基准目录: ${INCREMENTAL_BASEDIR}" >> ${LOG_FILE}
echo "增量目录: ${INCR_BACKUP_DIR}" >> ${LOG_FILE}

# 执行增量备份
echo "执行增量备份..." >> ${LOG_FILE}
xtrabackup \
  --backup \
  --target-dir=${INCR_BACKUP_DIR} \
  --incremental-basedir=${INCREMENTAL_BASEDIR} \
  --user=${MYSQL_USER} \
  --password=${MYSQL_PASSWORD} \
  --parallel=${THREADS} \
  --compress \
  --compress-threads=${THREADS} \
  --stream=xbstream \
  --extra-lsndir=${INCR_BACKUP_DIR} \
  2>> ${LOG_FILE} | gzip > ${INCR_BACKUP_DIR}/incremental.xbstream.gz

# 检查备份结果
if [ $? -eq 0 ]; then
    echo "增量备份成功" >> ${LOG_FILE}

    # 生成备份信息
    echo "备份时间: ${DATE}" > ${INCR_BACKUP_DIR}/backup.info
    echo "备份类型: 增量备份" >> ${INCR_BACKUP_DIR}/backup.info
    echo "基准目录: ${INCREMENTAL_BASEDIR}" >> ${INCR_BACKUP_DIR}/backup.info
    echo "LSN信息:" >> ${INCR_BACKUP_DIR}/backup.info
    cat ${INCR_BACKUP_DIR}/xtrabackup_checkpoints >> ${INCR_BACKUP_DIR}/backup.info

else
    echo "增量备份失败" >> ${LOG_FILE}
    rm -rf ${INCR_BACKUP_DIR}
    exit 1
fi

# 清理旧增量备份
echo "清理超过${RETENTION_DAYS}天的旧增量备份..." >> ${LOG_FILE}
find ${BACKUP_BASE}/incr_* -type d -mtime +${RETENTION_DAYS} -exec rm -rf {} \; 2>> ${LOG_FILE}

echo "增量备份完成: $(date)" >> ${LOG_FILE}
echo "备份大小: $(du -sh ${INCR_BACKUP_DIR} | awk '{print $1}')" >> ${LOG_FILE}
echo "==========================================" >> ${LOG_FILE}

恢复脚本 (xtrabackup_restore.sh)

#!/bin/bash
# xtrabackup_restore.sh - XtraBackup恢复脚本

# 配置参数
BACKUP_BASE="/data/backup/xtrabackup"
MYSQL_DATA_DIR="/var/lib/mysql"
MYSQL_USER="root"
MYSQL_PASSWORD="your_password"
RESTORE_DATE="$1"  # 从命令行参数获取恢复日期

# 检查参数
if [ -z "${RESTORE_DATE}" ]; then
    echo "用法: $0 <备份日期>"
    echo "示例: $0 20231201_120000"
    exit 1
fi

# 查找全量备份
FULL_BACKUP_DIR="${BACKUP_BASE}/full_${RESTORE_DATE}"
if [ ! -d "${FULL_BACKUP_DIR}" ]; then
    echo "未找到指定日期的全量备份: ${FULL_BACKUP_DIR}"
    exit 1
fi

echo "开始恢复数据库..."
echo "全量备份目录: ${FULL_BACKUP_DIR}"

# 停止MySQL服务
systemctl stop mysql

# 备份当前数据目录
if [ -d "${MYSQL_DATA_DIR}" ]; then
    mv ${MYSQL_DATA_DIR} ${MYSQL_DATA_DIR}_backup_$(date +%Y%m%d_%H%M%S)
fi

# 准备全量备份
echo "准备全量备份..."
xtrabackup --prepare --apply-log-only --target-dir=${FULL_BACKUP_DIR}

# 查找并应用增量备份
INCREMENTAL_BACKUPS=$(find ${BACKUP_BASE} -name "incr_*" -type d | grep -E "incr_.*${RESTORE_DATE:0:8}" | sort)

for INCR_DIR in ${INCREMENTAL_BACKUPS}; do
    if [ $(basename ${INCR_DIR}) \> "full_${RESTORE_DATE}" ]; then
        echo "应用增量备份: ${INCR_DIR}"
        xtrabackup --prepare --apply-log-only --target-dir=${FULL_BACKUP_DIR} \
          --incremental-dir=${INCR_DIR}
    fi
done

# 最终准备
echo "最终准备..."
xtrabackup --prepare --target-dir=${FULL_BACKUP_DIR}

# 恢复数据
echo "恢复数据到 ${MYSQL_DATA_DIR}..."
xtrabackup --copy-back --target-dir=${FULL_BACKUP_DIR}

# 设置权限
chown -R mysql:mysql ${MYSQL_DATA_DIR}

# 启动MySQL服务
systemctl start mysql

echo "数据库恢复完成!"

四、自动化备份配置

4.1 Crontab 配置示例

# 编辑crontab
crontab -e

# 添加以下内容:
# 每天凌晨2点全量备份
0 2 * * 0 /path/to/mysqldump_full.sh

# 每天凌晨1点增量备份(周日除外)
0 1 * * 1-6 /path/to/mysqldump_incremental.sh

# 每周日凌晨3点XtraBackup全量备份
0 3 * * 0 /path/to/xtrabackup_full.sh

# 每天凌晨4点XtraBackup增量备份
0 4 * * 1-6 /path/to/xtrabackup_incremental.sh

4.2 备份策略建议

# 推荐备份策略:
# 1. 每周日:全量备份
# 2. 周一至周六:增量备份
# 3. 保留4周的全量备份
# 4. 保留2周的增量备份
# 5. 定期测试恢复
# 6. 备份到远程存储

五、注意事项

  1. 测试备份恢复:定期测试备份文件是否可恢复
  2. 监控备份任务:监控备份日志和磁盘空间
  3. 加密敏感数据:备份文件包含敏感数据,建议加密存储
  4. 远程备份:将备份传输到远程服务器
  5. 版本兼容性:确保备份和恢复的MySQL版本一致
  6. 性能影响:备份期间可能影响数据库性能,建议在低峰期进行

六、常用检查命令

# 检查备份文件
ls -lh /data/backup/mysql/

# 查看备份日志
tail -f /var/log/mysql_backup.log

# 检查MySQL状态
mysql -u root -p -e "SHOW MASTER STATUS;"

# 检查二进制日志
ls -lh /var/lib/mysql/mysql-bin.*

# 验证备份文件完整性
gzip -t backup.sql.gz

这些脚本提供了完整的备份解决方案,可以根据实际需求进行调整和优化。

基于gitid的备份

GTID模式下的MySQL备份脚本

1. 全量备份脚本 (mysql_full_backup.sh)

#!/bin/bash
# mysql_full_backup.sh - GTID模式下的全量备份脚本
# 使用方法: ./mysql_full_backup.sh [备份路径]

# 配置参数
MYSQL_USER="root"
MYSQL_PASS="Root@1234"
MYSQL_HOST="localhost"
MYSQL_PORT="3306"
BACKUP_DIR="${1:-/data/backup/mysql}"
LOG_FILE="/var/log/mysql_backup.log"
DATE=$(date +%Y%m%d_%H%M%S)
FULL_BACKUP_DIR="${BACKUP_DIR}/full_${DATE}"

# 创建备份目录
mkdir -p "${FULL_BACKUP_DIR}"

# 日志函数
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"
}

# 检查MySQL连接
check_mysql() {
    if ! mysql -u"${MYSQL_USER}" -p"${MYSQL_PASS}" -h"${MYSQL_HOST}" -P"${MYSQL_PORT}" -e "SELECT 1" >/dev/null 2>&1; then
        log "ERROR: 无法连接到MySQL服务器"
        exit 1
    fi
}

# 获取GTID信息
get_gtid_info() {
    GTID_EXECUTED=$(mysql -u"${MYSQL_USER}" -p"${MYSQL_PASS}" -h"${MYSQL_HOST}" -P"${MYSQL_PORT}" -sN -e "SELECT @@GLOBAL.GTID_EXECUTED")
    echo "${GTID_EXECUTED}" > "${FULL_BACKUP_DIR}/gtid_executed.txt"
    log "GTID_EXECUTED: ${GTID_EXECUTED}"
}

# 执行全量备份
full_backup() {
    log "开始全量备份..."

    # 使用mysqldump进行全量备份(包含GTID信息)
    mysqldump -u"${MYSQL_USER}" -p"${MYSQL_PASS}" \
        -h"${MYSQL_HOST}" -P"${MYSQL_PORT}" \
        --single-transaction \
        --master-data=2 \
        --routines \
        --triggers \
        --events \
        --all-databases \
        --set-gtid-purged=ON \
        --flush-logs \
        > "${FULL_BACKUP_DIR}/full_backup.sql" 2>>"${LOG_FILE}"

    if [ $? -eq 0 ]; then
        log "全量备份完成: ${FULL_BACKUP_DIR}/full_backup.sql"

        # 压缩备份文件
        gzip "${FULL_BACKUP_DIR}/full_backup.sql"
        log "备份文件已压缩: ${FULL_BACKUP_DIR}/full_backup.sql.gz"

        # 生成恢复脚本
        create_restore_script
    else
        log "ERROR: 全量备份失败"
        exit 1
    fi
}

# 创建恢复脚本
create_restore_script() {
    cat > "${FULL_BACKUP_DIR}/restore.sh" << 'EOF'
#!/bin/bash
# MySQL全量备份恢复脚本
# 使用方法: ./restore.sh [备份文件]

BACKUP_FILE="${1}"
MYSQL_USER="root"
MYSQL_PASS="Root@1234"

if [ -z "${BACKUP_FILE}" ]; then
    echo "请指定备份文件: ./restore.sh /path/to/backup.sql.gz"
    exit 1
fi

if [ ! -f "${BACKUP_FILE}" ]; then
    echo "备份文件不存在: ${BACKUP_FILE}"
    exit 1
fi

echo "开始恢复MySQL数据库..."

# 解压备份文件
gunzip -c "${BACKUP_FILE}" > restore_temp.sql

# 重置GTID并导入
mysql -u"${MYSQL_USER}" -p"${MYSQL_PASS}" -e "RESET MASTER;"
mysql -u"${MYSQL_USER}" -p"${MYSQL_PASS}" < restore_temp.sql

# 清理临时文件
rm -f restore_temp.sql

echo "数据库恢复完成!"
EOF

    chmod +x "${FULL_BACKUP_DIR}/restore.sh"
    log "恢复脚本已生成: ${FULL_BACKUP_DIR}/restore.sh"
}

# 清理旧备份(保留最近7天)
clean_old_backups() {
    log "清理7天前的备份..."
    find "${BACKUP_DIR}" -name "full_*" -type d -mtime +7 | xargs rm -rf
    find "${BACKUP_DIR}" -name "incr_*" -type d -mtime +7 | xargs rm -rf
}

# 主函数
main() {
    log "===== 开始MySQL全量备份 ====="
    check_mysql
    get_gtid_info
    full_backup
    clean_old_backups
    log "===== 全量备份完成 ====="
}

main

2. 增量备份脚本 (mysql_incr_backup.sh)

#!/bin/bash
# mysql_incr_backup.sh - GTID模式下的增量备份脚本
# 使用方法: ./mysql_incr_backup.sh [全量备份路径] [增量备份路径]

# 配置参数
MYSQL_USER="root"
MYSQL_PASS="Root@1234"
MYSQL_HOST="localhost"
MYSQL_PORT="3306"
BASE_BACKUP_DIR="${1:-/data/backup/mysql}"
INCR_BACKUP_DIR="${BASE_BACKUP_DIR}/incremental"
LOG_FILE="/var/log/mysql_backup.log"
DATE=$(date +%Y%m%d_%H%M%S)
INCR_BACKUP_SUBDIR="${INCR_BACKUP_DIR}/incr_${DATE}"

# 创建备份目录
mkdir -p "${INCR_BACKUP_SUBDIR}"

# 日志函数
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"
}

# 检查上次备份的GTID位置
get_last_backup_gtid() {
    # 查找最新的全量备份目录
    LATEST_FULL_BACKUP=$(find "${BASE_BACKUP_DIR}" -name "full_*" -type d | sort -r | head -1)

    if [ -z "${LATEST_FULL_BACKUP}" ]; then
        log "ERROR: 未找到全量备份,请先执行全量备份"
        exit 1
    fi

    # 获取全量备份时的GTID
    GTID_FILE="${LATEST_FULL_BACKUP}/gtid_executed.txt"
    if [ -f "${GTID_FILE}" ]; then
        LAST_GTID=$(cat "${GTID_FILE}")
        log "上次备份的GTID: ${LAST_GTID}"
        echo "${LAST_GTID}"
    else
        log "ERROR: 在全量备份中未找到GTID信息"
        exit 1
    fi
}

# 获取当前GTID信息
get_current_gtid() {
    GTID_EXECUTED=$(mysql -u"${MYSQL_USER}" -p"${MYSQL_PASS}" -h"${MYSQL_HOST}" -P"${MYSQL_PORT}" -sN -e "SELECT @@GLOBAL.GTID_EXECUTED")
    log "当前GTID_EXECUTED: ${GTID_EXECUTED}"
    echo "${GTID_EXECUTED}"
}

# 执行增量备份
incremental_backup() {
    log "开始增量备份..."

    # 获取binlog文件列表
    BINLOG_INDEX=$(mysql -u"${MYSQL_USER}" -p"${MYSQL_PASS}" -h"${MYSQL_HOST}" -P"${MYSQL_PORT}" -sN -e "SHOW VARIABLES LIKE 'log_bin_basename'" | awk '{print $2}')
    BINLOG_DIR=$(dirname "${BINLOG_INDEX}")

    if [ -z "${BINLOG_INDEX}" ]; then
        log "ERROR: 未启用binlog,无法进行增量备份"
        exit 1
    fi

    # 刷新日志,生成新的binlog文件
    mysql -u"${MYSQL_USER}" -p"${MYSQL_PASS}" -h"${MYSQL_HOST}" -P"${MYSQL_PORT}" -e "FLUSH BINARY LOGS;"

    # 获取当前的binlog文件
    CURRENT_BINLOG=$(mysql -u"${MYSQL_USER}" -p"${MYSQL_PASS}" -h"${MYSQL_HOST}" -P"${MYSQL_PORT}" -sN -e "SHOW MASTER STATUS" | awk '{print $1}')

    # 复制从上个GTID之后的所有binlog文件
    log "开始复制binlog文件..."
    for binlog in $(ls "${BINLOG_DIR}"/*.0* 2>/dev/null | sort); do
        if [[ "${binlog}" > "${BINLOG_DIR}/${CURRENT_BINLOG}" ]] || [[ "${binlog}" == "${BINLOG_DIR}/${CURRENT_BINLOG}" ]]; then
            continue
        fi
        cp "${binlog}" "${INCR_BACKUP_SUBDIR}/"
        log "已复制: $(basename ${binlog})"
    done

    # 使用mysqlbinlog导出指定GTID之后的SQL
    LAST_GTID="$1"
    CURRENT_GTID="$2"

    if [ -n "${LAST_GTID}" ] && [ -n "${CURRENT_GTID}" ] && [ "${LAST_GTID}" != "${CURRENT_GTID}" ]; then
        log "导出从GTID ${LAST_GTID}${CURRENT_GTID} 的增量SQL..."

        # 导出增量SQL
        mysqlbinlog --read-from-remote-server \
            --host="${MYSQL_HOST}" \
            --port="${MYSQL_PORT}" \
            --user="${MYSQL_USER}" \
            --password="${MYSQL_PASS}" \
            --include-gtids="${LAST_GTID}-${CURRENT_GTID}" \
            --result-file="${INCR_BACKUP_SUBDIR}/incremental.sql" \
            --verbose

        if [ $? -eq 0 ]; then
            log "增量SQL导出完成: ${INCR_BACKUP_SUBDIR}/incremental.sql"

            # 压缩文件
            gzip "${INCR_BACKUP_SUBDIR}/incremental.sql"
        else
            log "WARNING: 增量SQL导出失败,继续使用binlog文件备份"
        fi
    fi

    # 备份GTID信息
    echo "${CURRENT_GTID}" > "${INCR_BACKUP_SUBDIR}/current_gtid.txt"

    log "增量备份完成: ${INCR_BACKUP_SUBDIR}"
}

# 创建增量恢复脚本
create_incremental_restore_script() {
    cat > "${INCR_BACKUP_SUBDIR}/restore_incremental.sh" << 'EOF'
#!/bin/bash
# MySQL增量备份恢复脚本
# 使用方法: 
# 1. 先恢复全量备份: ./restore.sh /path/to/full_backup.sql.gz
# 2. 再恢复增量: ./restore_incremental.sh [增量备份目录]

INCR_BACKUP_DIR="$1"
MYSQL_USER="root"
MYSQL_PASS="Root@1234"

if [ -z "${INCR_BACKUP_DIR}" ] || [ ! -d "${INCR_BACKUP_DIR}" ]; then
    echo "请指定增量备份目录"
    exit 1
fi

echo "开始恢复增量备份..."

# 恢复binlog文件
if ls "${INCR_BACKUP_DIR}"/*.0* 1> /dev/null 2>&1; then
    echo "恢复binlog文件..."
    for binlog in "${INCR_BACKUP_DIR}"/*.0*; do
        echo "恢复: $(basename ${binlog})"
        mysqlbinlog "${binlog}" | mysql -u"${MYSQL_USER}" -p"${MYSQL_PASS}"
    done
fi

# 恢复增量SQL文件
if [ -f "${INCR_BACKUP_DIR}/incremental.sql.gz" ]; then
    echo "恢复增量SQL..."
    gunzip -c "${INCR_BACKUP_DIR}/incremental.sql.gz" | mysql -u"${MYSQL_USER}" -p"${MYSQL_PASS}"
fi

echo "增量恢复完成!"
EOF

    chmod +x "${INCR_BACKUP_SUBDIR}/restore_incremental.sh"
}

# 主函数
main() {
    log "===== 开始MySQL增量备份 ====="

    LAST_GTID=$(get_last_backup_gtid)
    CURRENT_GTID=$(get_current_gtid)

    if [ "${LAST_GTID}" = "${CURRENT_GTID}" ]; then
        log "INFO: 自上次备份后没有新数据,跳过增量备份"
        rm -rf "${INCR_BACKUP_SUBDIR}"
        exit 0
    fi

    incremental_backup "${LAST_GTID}" "${CURRENT_GTID}"
    create_incremental_restore_script

    log "===== 增量备份完成 ====="
}

main

3. 自动化备份管理脚本 (mysql_backup_manager.sh)

#!/bin/bash
# mysql_backup_manager.sh - 备份管理脚本
# 使用方法: 
# 1. 全量备份: ./mysql_backup_manager.sh full
# 2. 增量备份: ./mysql_backup_manager.sh incr
# 3. 自动模式: ./mysql_backup_manager.sh auto

# 配置参数
BACKUP_DIR="/data/backup/mysql"
LOG_FILE="/var/log/mysql_backup.log"
FULL_SCRIPT="/opt/scripts/mysql_full_backup.sh"
INCR_SCRIPT="/opt/scripts/mysql_incr_backup.sh"

# 日志函数
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"
}

# 检查备份目录
check_backup_dir() {
    if [ ! -d "${BACKUP_DIR}" ]; then
        mkdir -p "${BACKUP_DIR}"
        log "创建备份目录: ${BACKUP_DIR}"
    fi
}

# 全量备份
do_full_backup() {
    log "执行全量备份..."
    if [ -x "${FULL_SCRIPT}" ]; then
        "${FULL_SCRIPT}" "${BACKUP_DIR}"
    else
        log "ERROR: 全量备份脚本不存在: ${FULL_SCRIPT}"
        exit 1
    fi
}

# 增量备份
do_incremental_backup() {
    log "执行增量备份..."
    if [ -x "${INCR_SCRIPT}" ]; then
        "${INCR_SCRIPT}" "${BACKUP_DIR}"
    else
        log "ERROR: 增量备份脚本不存在: ${INCR_SCRIPT}"
        exit 1
    fi
}

# 自动模式(每周日全量,其他天增量)
do_auto_backup() {
    DAY_OF_WEEK=$(date +%u)  # 1-7, 1=Monday, 7=Sunday

    if [ "${DAY_OF_WEEK}" -eq 7 ]; then
        log "周日,执行全量备份"
        do_full_backup
    else
        log "周${DAY_OF_WEEK},执行增量备份"
        do_incremental_backup
    fi
}

# 备份状态检查
check_backup_status() {
    log "=== 备份状态检查 ==="

    # 检查最新全量备份
    LATEST_FULL=$(find "${BACKUP_DIR}" -name "full_*" -type d | sort -r | head -1)
    if [ -n "${LATEST_FULL}" ]; then
        log "最新全量备份: $(basename ${LATEST_FULL})"
        log "备份时间: $(stat -c %y ${LATEST_FULL})"
    else
        log "WARNING: 未找到全量备份"
    fi

    # 检查最新增量备份
    LATEST_INCR=$(find "${BACKUP_DIR}" -name "incr_*" -type d | sort -r | head -1)
    if [ -n "${LATEST_INCR}" ]; then
        log "最新增量备份: $(basename ${LATEST_INCR})"
        log "备份时间: $(stat -c %y ${LATEST_INCR})"
    fi

    # 检查磁盘空间
    DISK_USAGE=$(df -h "${BACKUP_DIR}" | tail -1 | awk '{print $5}')
    log "备份目录磁盘使用率: ${DISK_USAGE}"
}

# 主函数
main() {
    ACTION="${1:-auto}"

    case "${ACTION}" in
        "full")
            check_backup_dir
            do_full_backup
            ;;
        "incr")
            check_backup_dir
            do_incremental_backup
            ;;
        "auto")
            check_backup_dir
            do_auto_backup
            ;;
        "status")
            check_backup_status
            ;;
        *)
            echo "用法: $0 {full|incr|auto|status}"
            echo "  full    - 执行全量备份"
            echo "  incr    - 执行增量备份"
            echo "  auto    - 自动模式(默认)"
            echo "  status  - 检查备份状态"
            exit 1
            ;;
    esac
}

main "$@"

4. 安装和配置脚本

#!/bin/bash
# setup_mysql_backup.sh - 安装配置备份脚本

# 创建目录
mkdir -p /opt/scripts
mkdir -p /data/backup/mysql
mkdir -p /var/log/mysql_backup

# 复制脚本
cp mysql_full_backup.sh /opt/scripts/
cp mysql_incr_backup.sh /opt/scripts/
cp mysql_backup_manager.sh /opt/scripts/

# 设置权限
chmod +x /opt/scripts/*.sh
chown -R root:root /opt/scripts/
chown -R mysql:mysql /data/backup/mysql/

# 创建日志文件
touch /var/log/mysql_backup.log
chown mysql:mysql /var/log/mysql_backup.log

# 配置cron定时任务
cat > /etc/cron.d/mysql-backup << 'EOF'
# MySQL自动备份计划
# 每天凌晨2点执行自动备份
0 2 * * * root /opt/scripts/mysql_backup_manager.sh auto >> /var/log/mysql_backup/cron.log 2>&1

# 每周日凌晨1点执行全量备份
0 1 * * 0 root /opt/scripts/mysql_backup_manager.sh full >> /var/log/mysql_backup/cron.log 2>&1

# 每天凌晨3点清理旧备份
0 3 * * * root find /data/backup/mysql -type d -name "full_*" -mtime +30 -exec rm -rf {} \;
0 3 * * * root find /data/backup/mysql -type d -name "incr_*" -mtime +7 -exec rm -rf {} \;
EOF

echo "安装完成!"
echo "手动执行测试:"
echo "1. 全量备份: /opt/scripts/mysql_backup_manager.sh full"
echo "2. 增量备份: /opt/scripts/mysql_backup_manager.sh incr"
echo "3. 查看状态: /opt/scripts/mysql_backup_manager.sh status"

5. 恢复指南

# 恢复流程:
# 1. 找到最新的全量备份
LATEST_FULL=$(find /data/backup/mysql -name "full_*" -type d | sort -r | head -1)

# 2. 恢复全量备份
cd "${LATEST_FULL}"
./restore.sh full_backup.sql.gz

# 3. 按顺序恢复增量备份
for incr_dir in $(find /data/backup/mysql -name "incr_*" -type d | sort); do
    cd "${incr_dir}"
    ./restore_incremental.sh .
done

使用说明

  1. 首次使用

    chmod +x setup_mysql_backup.sh
    ./setup_mysql_backup.sh
  2. 修改配置:编辑脚本中的数据库连接信息

  3. 手动备份测试

    # 全量备份
    /opt/scripts/mysql_backup_manager.sh full
    
    # 增量备份
    /opt/scripts/mysql_backup_manager.sh incr
    
    # 查看备份状态
    /opt/scripts/mysql_backup_manager.sh status
  4. 查看备份文件

    ls -la /data/backup/mysql/

注意事项

  1. GTID模式必须启用:确保MySQL配置中启用了GTID
  2. binlog必须开启:增量备份依赖binlog
  3. 定期清理:脚本会自动清理7天前的增量备份和30天前的全量备份
  4. 备份验证:定期测试恢复流程
  5. 监控磁盘空间:确保备份目录有足够空间
  6. 主从复制:在主库执行备份,从库可用于恢复测试

这些脚本已针对GTID模式优化,确保备份和恢复时GTID一致性。

不一样!XtraBackup在GTID和非GTID模式下的备份脚本有明显差异。

主要差异对比表

特性 GTID模式 非GTID模式
备份命令参数 需要特殊GTID处理 常规参数即可
恢复流程 必须重置GTID 无需GTID处理
主从复制设置 基于GTID设置 基于binlog位置
一致性要求 GTID一致性 位置一致性

GTID模式下的XtraBackup脚本

1. GTID全量备份脚本 (xtrabackup_gtid_full.sh)

#!/bin/bash
# GTID模式全量备份脚本

# 配置
MYSQL_USER="backup"
MYSQL_PASS="Backup@123"
BACKUP_DIR="/data/backup/xtrabackup"
LOG_FILE="/var/log/xtrabackup.log"
DATE=$(date +%Y%m%d_%H%M%S)
FULL_DIR="${BACKUP_DIR}/full_gtid_${DATE}"

# 创建目录
mkdir -p "${FULL_DIR}"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"
}

# 1. GTID模式专用备份
log "开始GTID模式全量备份..."

# 关键参数:--set-gtid-purged=ON
xtrabackup \
    --backup \
    --user="${MYSQL_USER}" \
    --password="${MYSQL_PASS}" \
    --target-dir="${FULL_DIR}" \
    --datadir=/var/lib/mysql \
    --set-gtid-purged=ON \
    --extra-lsndir="${FULL_DIR}/lsn" \
    --stream=xbstream \
    --compress \
    2>> "${LOG_FILE}" | gzip > "${FULL_DIR}/backup.xbstream.gz"

# 或者使用压缩格式
# xtrabackup \
#     --backup \
#     --user="${MYSQL_USER}" \
#     --password="${MYSQL_PASS}" \
#     --target-dir="${FULL_DIR}" \
#     --set-gtid-purged=ON \
#     --compress \
#     --compress-threads=4

if [ $? -eq 0 ]; then
    # 2. 准备备份
    xtrabackup \
        --prepare \
        --target-dir="${FULL_DIR}" \
        --apply-log-only

    # 3. 保存GTID信息
    cat > "${FULL_DIR}/gtid_info.txt" << EOF
# GTID备份信息
备份时间: $(date)
备份类型: GTID全量备份
GTID模式: ON
恢复前需执行: RESET MASTER;
EOF

    # 获取当前GTID
    GTID_EXECUTED=$(mysql -u${MYSQL_USER} -p${MYSQL_PASS} -sN -e "SELECT @@GLOBAL.GTID_EXECUTED")
    echo "GTID_EXECUTED=${GTID_EXECUTED}" >> "${FULL_DIR}/gtid_info.txt"

    # 4. 创建恢复脚本
    cat > "${FULL_DIR}/restore_gtid.sh" << 'EOF'
#!/bin/bash
# GTID模式恢复脚本

BACKUP_DIR="$(cd "$(dirname "$0")" && pwd)"
MYSQL_USER="backup"
MYSQL_PASS="Backup@123"
DATA_DIR="/var/lib/mysql"

echo "正在停止MySQL服务..."
systemctl stop mysqld

echo "清空数据目录..."
rm -rf ${DATA_DIR}/*

echo "准备恢复备份..."
# 解压(如果备份是压缩的)
if [ -f "${BACKUP_DIR}/backup.xbstream.gz" ]; then
    echo "解压备份文件..."
    gzip -d -c "${BACKUP_DIR}/backup.xbstream.gz" | xbstream -x -C ${DATA_DIR}
fi

echo "应用日志..."
xtrabackup --prepare --target-dir=${DATA_DIR}

echo "修改文件权限..."
chown -R mysql:mysql ${DATA_DIR}

echo "重置GTID(重要!)..."
# 必须重置GTID,否则会冲突
cat > /tmp/reset_gtid.sql << SQL
RESET MASTER;
SET @@GLOBAL.GTID_PURGED='';
SQL

echo "启动MySQL服务..."
systemctl start mysqld

echo "等待MySQL启动..."
sleep 10

echo "恢复完成!"
echo "请检查GTID状态:SHOW GLOBAL VARIABLES LIKE 'gtid%';"
EOF

    chmod +x "${FULL_DIR}/restore_gtid.sh"
    log "GTID全量备份完成: ${FULL_DIR}"
else
    log "备份失败!"
    exit 1
fi

2. GTID增量备份脚本 (xtrabackup_gtid_incremental.sh)

#!/bin/bash
# GTID模式增量备份脚本

# 配置
MYSQL_USER="backup"
MYSQL_PASS="Backup@123"
BACKUP_DIR="/data/backup/xtrabackup"
LOG_FILE="/var/log/xtrabackup.log"
DATE=$(date +%Y%m%d_%H%M%S)

# 查找最新的全量备份
LATEST_FULL=$(find "${BACKUP_DIR}" -name "full_gtid_*" -type d | sort -r | head -1)

if [ -z "${LATEST_FULL}" ]; then
    echo "未找到全量备份,请先执行全量备份"
    exit 1
fi

# 增量备份目录
INCR_DIR="${BACKUP_DIR}/incr_gtid_${DATE}"

# 获取增量备份的LSN
BASE_LSN=$(cat "${LATEST_FULL}/xtrabackup_checkpoints" | grep "to_lsn" | awk '{print $3}')

log "开始GTID增量备份,基于LSN: ${BASE_LSN}"

# 执行增量备份
xtrabackup \
    --backup \
    --user="${MYSQL_USER}" \
    --password="${MYSQL_PASS}" \
    --target-dir="${INCR_DIR}" \
    --incremental-basedir="${LATEST_FULL}" \
    --set-gtid-purged=ON \
    --extra-lsndir="${INCR_DIR}/lsn"

# 保存增量备份信息
cat > "${INCR_DIR}/incremental_info.txt" << EOF
增量备份信息
------------
备份时间: $(date)
基础备份: $(basename ${LATEST_FULL})
基础LSN: ${BASE_LSN}
当前GTID: $(mysql -u${MYSQL_USER} -p${MYSQL_PASS} -sN -e "SELECT @@GLOBAL.GTID_EXECUTED")
EOF

非GTID模式下的XtraBackup脚本

1. 非GTID全量备份脚本 (xtrabackup_normal_full.sh)

#!/bin/bash
# 非GTID模式全量备份脚本

# 配置
MYSQL_USER="backup"
MYSQL_PASS="Backup@123"
BACKUP_DIR="/data/backup/xtrabackup"
LOG_FILE="/var/log/xtrabackup.log"
DATE=$(date +%Y%m%d_%H%M%S)
FULL_DIR="${BACKUP_DIR}/full_normal_${DATE}"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"
}

# 1. 非GTID备份(不需要--set-gtid-purged参数)
log "开始非GTID模式全量备份..."

xtrabackup \
    --backup \
    --user="${MYSQL_USER}" \
    --password="${MYSQL_PASS}" \
    --target-dir="${FULL_DIR}" \
    --datadir=/var/lib/mysql \
    --stream=xbstream \
    2>> "${LOG_FILE}" | gzip > "${FULL_DIR}/backup.xbstream.gz"

if [ $? -eq 0 ]; then
    # 2. 准备备份
    xtrabackup \
        --prepare \
        --target-dir="${FULL_DIR}"

    # 3. 保存binlog位置信息
    echo "=== binlog位置信息 ===" > "${FULL_DIR}/binlog_info.txt"
    cat "${FULL_DIR}/xtrabackup_binlog_info" >> "${FULL_DIR}/binlog_info.txt"

    # 4. 创建非GTID恢复脚本
    cat > "${FULL_DIR}/restore_normal.sh" << 'EOF'
#!/bin/bash
# 非GTID模式恢复脚本

BACKUP_DIR="$(cd "$(dirname "$0")" && pwd)"
MYSQL_USER="backup"
MYSQL_PASS="Backup@123"
DATA_DIR="/var/lib/mysql"

echo "正在停止MySQL服务..."
systemctl stop mysqld

echo "清空数据目录..."
rm -rf ${DATA_DIR}/*

echo "恢复备份..."
if [ -f "${BACKUP_DIR}/backup.xbstream.gz" ]; then
    gzip -d -c "${BACKUP_DIR}/backup.xbstream.gz" | xbstream -x -C ${DATA_DIR}
fi

echo "应用日志..."
xtrabackup --prepare --target-dir=${DATA_DIR}

echo "修改文件权限..."
chown -R mysql:mysql ${DATA_DIR}

echo "启动MySQL服务..."
systemctl start mysqld

echo "恢复完成!"
echo "binlog位置信息:"
cat ${BACKUP_DIR}/binlog_info.txt
EOF

    chmod +x "${FULL_DIR}/restore_normal.sh"
    log "非GTID全量备份完成: ${FULL_DIR}"
else
    log "备份失败!"
    exit 1
fi

2. 非GTID增量备份脚本 (xtrabackup_normal_incremental.sh)

#!/bin/bash
# 非GTID模式增量备份脚本

# 配置
MYSQL_USER="backup"
MYSQL_PASS="Backup@123"
BACKUP_DIR="/data/backup/xtrabackup"
LOG_FILE="/var/log/xtrabackup.log"
DATE=$(date +%Y%m%d_%H%M%S)

# 查找最新的全量备份
LATEST_FULL=$(find "${BACKUP_DIR}" -name "full_normal_*" -type d | sort -r | head -1)

if [ -z "${LATEST_FULL}" ]; then
    echo "未找到全量备份,请先执行全量备份"
    exit 1
fi

# 增量备份目录
INCR_DIR="${BACKUP_DIR}/incr_normal_${DATE}"

log "开始非GTID增量备份..."

# 执行增量备份
xtrabackup \
    --backup \
    --user="${MYSQL_USER}" \
    --password="${MYSQL_PASS}" \
    --target-dir="${INCR_DIR}" \
    --incremental-basedir="${LATEST_FULL}"

# 保存增量备份信息
echo "=== 增量备份信息 ===" > "${INCR_DIR}/incremental_info.txt"
echo "基础备份: $(basename ${LATEST_FULL})" >> "${INCR_DIR}/incremental_info.txt"
echo "binlog位置: $(cat ${LATEST_FULL}/xtrabackup_binlog_info)" >> "${INCR_DIR}/incremental_info.txt"

关键差异详解

1. 备份时的关键参数差异

# GTID模式必须添加
--set-gtid-purged=ON
# 或
--set-gtid-purged=OFF
# 或
--set-gtid-purged=AUTO

# 非GTID模式不需要此参数

2. 恢复时的关键差异

GTID恢复脚本必须包含:

# 重置GTID
mysql -e "RESET MASTER;"
# 或者导入备份的GTID
mysql -e "SET GLOBAL gtid_purged='备份的GTID值';"

非GTID恢复只需要常规步骤:

# 直接恢复数据文件即可
xtrabackup --copy-back --target-dir=/backup

3. 主从复制设置差异

GTID模式设置从库:

-- 不需要指定binlog位置
CHANGE MASTER TO
  MASTER_HOST='master_host',
  MASTER_USER='repl',
  MASTER_PASSWORD='password',
  MASTER_AUTO_POSITION=1;  -- 关键参数!

非GTID模式设置从库:

-- 需要指定binlog文件和位置
CHANGE MASTER TO
  MASTER_HOST='master_host',
  MASTER_USER='repl',
  MASTER_PASSWORD='password',
  MASTER_LOG_FILE='mysql-bin.000001',
  MASTER_LOG_POS=123456;

统一备份脚本(自动检测模式)

#!/bin/bash
# xtrabackup_unified.sh - 自动检测GTID模式的统一备份脚本

# 检测MySQL GTID模式
check_gtid_mode() {
    GTID_MODE=$(mysql -u${MYSQL_USER} -p${MYSQL_PASS} -sN -e "SELECT @@GLOBAL.GTID_MODE")
    if [ "${GTID_MODE}" = "ON" ]; then
        echo "gtid"
    else
        echo "normal"
    fi
}

# 主备份函数
backup_mysql() {
    MODE=$(check_gtid_mode)
    DATE=$(date +%Y%m%d_%H%M%S)

    if [ "${MODE}" = "gtid" ]; then
        log "检测到GTID模式,执行GTID备份"
        BACKUP_DIR="${BASE_DIR}/full_gtid_${DATE}"
        XTRA_OPTIONS="--set-gtid-purged=ON"
    else
        log "检测到非GTID模式,执行常规备份"
        BACKUP_DIR="${BASE_DIR}/full_normal_${DATE}"
        XTRA_OPTIONS=""
    fi

    # 执行备份
    xtrabackup \
        --backup \
        --user="${MYSQL_USER}" \
        --password="${MYSQL_PASS}" \
        --target-dir="${BACKUP_DIR}" \
        ${XTRA_OPTIONS}

    # 保存模式信息
    echo "BACKUP_MODE=${MODE}" > "${BACKUP_DIR}/backup_mode.txt"
    echo "GTID_EXECUTED=$(mysql -u${MYSQL_USER} -p${MYSQL_PASS} -sN -e "SELECT @@GLOBAL.GTID_EXECUTED")" >> "${BACKUP_DIR}/gtid_info.txt"
}

# 主恢复函数
restore_mysql() {
    BACKUP_PATH="$1"
    MODE=$(cat "${BACKUP_PATH}/backup_mode.txt" | grep "BACKUP_MODE" | cut -d'=' -f2)

    if [ "${MODE}" = "gtid" ]; then
        log "GTID模式恢复,需要重置GTID"
        systemctl stop mysqld
        rm -rf /var/lib/mysql/*
        xtrabackup --copy-back --target-dir=${BACKUP_PATH}
        chown -R mysql:mysql /var/lib/mysql
        systemctl start mysqld
        # GTID特殊处理
        mysql -e "RESET MASTER;"
    else
        log "非GTID模式恢复"
        systemctl stop mysqld
        rm -rf /var/lib/mysql/*
        xtrabackup --copy-back --target-dir=${BACKUP_PATH}
        chown -R mysql:mysql /var/lib/mysql
        systemctl start mysqld
    fi
}

建议

  1. 生产环境:为GTID和非GTID模式分别准备不同的备份脚本
  2. 备份策略:无论哪种模式,都应该:
    • 定期执行全量备份
    • 定期测试恢复流程
    • 监控备份完整性
  3. 文档记录:明确记录数据库运行模式
  4. 备份验证:恢复时特别注意GTID处理

核心原则:GTID模式下必须处理GTID一致性,非GTID模式下不需要关心GTID。

作者:严锋  创建时间:2025-12-25 10:39
最后编辑:严锋  更新时间:2025-12-25 10:40