XtraBackup 备份 Mysql 数据

下载安装

官网 Percona XtraBackup 

选择好对应版本 DOWNLOAD ALL PACKAGES      

注意:XtraBackup的版本必须和MySQL版本一致,且备份之后还原MySQL版本也需要一致

# 创建文件夹
mkdir -p /opt/xtrabackup/full
​
#将下好的tar包进行解压到/xtraBackup文件下
tar -xf Percona-XtraBackup-8.4.0-3-rcccec763-el8-x86_64-bundle.tar -C /opt/xtrabackup
​
# 安装
cd /opt/xtrabackup
yum install -y percona*
​
# 查看
xtrabackup -version
​

 

一、全量备份

备份

# 设置权限mysql GRANT权限
mysql -uroot -p use mysql; 
update user set host='%' where user='root' ; GRANT BACKUP_ADMIN ON . TO 'root'@'%'
FLUSH PRIVILEGES;


# 备份 执行正确最后会显示 completed OK!
xtrabackup --backup \
  --user=root \
  --password='password' \
  --target-dir=/opt/xtrabackup/full
 

恢复

#1、停止 MySQL 服务
systemctl stop mysqld
​
#2. 备份现有数据(可选但强烈建议)
mv /var/lib/mysql /var/lib/mysql_backup_$(date +%s)
mkdir /var/lib/mysql
chown mysql:mysql /var/lib/mysql
​
# 3恢复
#3.1. 准备全量备份
xtrabackup --prepare --target-dir=/opt/xtrabackup/full
#3.2 恢复
xtrabackup --copy-back --target-dir=/opt/xtrabackup/full
#3.3 权限
chown -R mysql:mysql /var/lib/mysql
chmod -R 750 /var/lib/mysql
#3.4 启动
systemctl start mysqld

 

 

二、增量备份

增量备份第一次

# 创建第一次增量备份目录
mkdir -p /opt/xtrabackup/incremental_backup_1_20250618
​
# 执行第一次增量备份
xtrabackup --user=root --password='password' --backup --target-dir=/opt/xtrabackup/incremental_backup_1_20250618 --incremental-basedir=/opt/xtrabackup/full
​
# 对第一次增量备份进行prepare
xtrabackup --prepare --target-dir=/opt/xtrabackup/incremental_backup_1_20250618

增量备份第二次

# 创建第二次增量备份目录
mkdir -p /opt/xtrabackup/incremental_backup_2_20250618
​
# 执行第二次增量备份
xtrabackup --user=root --password='password' --backup --target-dir=/opt/xtrabackup/incremental_backup_2_20250618 --incremental-basedir=/opt/xtrabackup/incremental_backup_1_20250618
​
# 对第二次增量备份进行prepare
xtrabackup --prepare --target-dir=/opt/xtrabackup/incremental_backup_2_20250618

合并增量备份到全量备份

# 对全量备份进行初步prepare,允许后续添加增量
xtrabackup --prepare --apply-log-only --target-dir=/opt/xtrabackup/full
​
# 首先将第一次增量合并到全量备份
xtrabackup --prepare --target-dir=/opt/xtrabackup/full --incremental-dir=/opt/xtrabackup/incremental_backup_1_20250618
​
# 然后将第二次增量合并到全量备份
xtrabackup --prepare --target-dir=/opt/xtrabackup/full --incremental-dir=/opt/xtrabackup/incremental_backup_2_20250618
​
# 执行最终的prepare,不使用--apply-log-only参数
xtrabackup --prepare --target-dir=/opt/xtrabackup/full

数据恢复

#1、停止 MySQL 服务
systemctl stop mysqld
​
#2. 备份现有数据(可选但强烈建议)
mv /var/lib/mysql /var/lib/mysql_backup_$(date +%s)
mkdir /var/lib/mysql
chown mysql:mysql /var/lib/mysql
​
# 3恢复
#3.1. 准备全量备份
xtrabackup --prepare --target-dir=/opt/xtrabackup/full
#3.2 恢复
xtrabackup --copy-back --target-dir=/opt/xtrabackup/full
#3.3 权限
chown -R mysql:mysql /var/lib/mysql
chmod -R 750 /var/lib/mysql
#3.4 启动
systemctl start mysqld

 

 

三、备份恢复脚本

数据备份

 主要功能:每月全量备份数据到文件夹,之后再增量备份,如果找不到全量备份则先全量备份,之后压缩上传服务器(结合定时任务做到每日备份一次)

#!/bin/bash

# XtraBackup级联增量备份脚本
# 功能:每月1号执行全量备份,其余时间执行基于前一个备份的增量备份
#       自动识别最后一次备份路径,支持全量+级联增量的备份策略
#       备份文件包含日期和时分秒,便于识别
#       备份文件全部放到年月命名的文件夹下,如:202506
#       传输到远程服务器后删除压缩包,保留原始备份

# 配置信息
USER="root"               # MySQL用户名
PASSWORD="123456"       # MySQL密码
BACKUP_DIR="/opt/xtrabackup"  # 本地备份目录
REMOTE_SERVER="192.168.10.55" # 远程服务器IP
REMOTE_DIR="/opt/xtrabackup/53apfuture"    # 远程备份目录
REMOTE_USER="root"      # 远程服务器用户名

# 日志文件
LOG_FILE="/opt/xtrabackup/xtrabackup.log"

# 获取当前日期和时间
DATE=$(date +%Y%m%d)
DATETIME=$(date +%Y%m%d_%H%M%S)
YEAR_MONTH=$(date +%Y%m)

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

# 错误处理函数
handle_error() {
    log "ERROR: $1"
    echo "错误: $1" >&2
    exit 1
}

# 检查命令执行状态
check_status() {
    if [ $? -ne 0 ]; then
        handle_error "$1"
    else
        log "$2"
    fi
}

# 创建年月目录(如果不存在)
create_month_dir() {
    local month_dir="$BACKUP_DIR/$YEAR_MONTH"
    
    if [ ! -d "$month_dir" ]; then
        log "创建年月目录: $month_dir"
        mkdir -p "$month_dir"
        check_status "创建年月目录失败" "创建年月目录成功"
    fi
    
    # 检查目录权限
    if [ ! -w "$month_dir" ]; then
        log "修改目录权限: $month_dir"
        chmod 755 "$month_dir"
        check_status "修改目录权限失败" "修改目录权限成功"
    fi
}

# 查找最近一次备份(可以是全量或增量)
find_last_backup() {
    # 先创建年月目录
    create_month_dir
    
    # 查找最近的备份目录(全量或增量)
    local latest_backup=$(ls -td $BACKUP_DIR/$YEAR_MONTH/xtrabackup_{full,add}_* 2>/dev/null | grep -v '\.tar\.gz' | head -1)
    
    if [ -n "$latest_backup" ]; then
        log "找到最近的备份: $latest_backup"
        echo "$latest_backup"
        return 0
    else
        log "未找到任何备份,将执行全量备份"
        return 1
    fi
}

# 全量备份函数
full_backup() {
    # 先创建年月目录
    create_month_dir
    
    local backup_name="xtrabackup_full_$DATETIME"
    local backup_path="$BACKUP_DIR/$YEAR_MONTH/$backup_name"
    local tar_name="xtrabackup_full_$DATETIME.tar.gz"
    local tar_path="$BACKUP_DIR/$YEAR_MONTH/$tar_name"
    
    log "开始全量备份: $backup_path"
    
    # 检查并清理目标目录
    if [ -d "$backup_path" ]; then
        log "清理已有备份目录: $backup_path"
        rm -rf "$backup_path"
    fi
    
    # 执行全量备份
    xtrabackup --user=$USER --password=$PASSWORD --backup --target-dir=$backup_path
    check_status "全量备份失败" "全量备份成功"
    
    # 创建压缩包
    log "创建压缩包: $tar_path"
    tar -zcf $tar_path -C $BACKUP_DIR/$YEAR_MONTH $backup_name
    check_status "创建压缩包失败" "创建压缩包成功"
    
    # 传输到远程服务器
    log "开始传输到远程服务器: $REMOTE_SERVER:$REMOTE_DIR"
    scp $tar_path $REMOTE_USER@$REMOTE_SERVER:$REMOTE_DIR/
    check_status "传输到远程服务器失败" "传输到远程服务器成功"
    
    # 传输成功后,删除本地压缩包,保留原始备份目录
    log "删除本地压缩包: $tar_path"
    rm -f $tar_path
}

# 增量备份函数(基于前一个备份)
incremental_backup() {
    # 先创建年月目录
    create_month_dir
    
    local last_backup=$(find_last_backup)
    if [ $? -ne 0 ]; then
        log "未找到可基于的备份,执行全量备份"
        full_backup
        return
    fi
    
    local backup_name="xtrabackup_add_$DATETIME"
    local backup_path="$BACKUP_DIR/$YEAR_MONTH/$backup_name"
    local base_dir=$(basename $last_backup)
    local tar_name="xtrabackup_add_$DATETIME.tar.gz"
    local tar_path="$BACKUP_DIR/$YEAR_MONTH/$tar_name"
    
    log "开始增量备份: $backup_path (基于 $base_dir)"
    
    # 检查并清理目标目录
    if [ -d "$backup_path" ]; then
        log "清理已有备份目录: $backup_path"
        rm -rf "$backup_path"
    fi
    
    # 执行增量备份(基于前一个备份)
    xtrabackup --user=$USER --password=$PASSWORD --backup --target-dir=$backup_path \
        --incremental-basedir=$last_backup
    check_status "增量备份失败" "增量备份成功"
    
    # 验证增量备份的LSN连续性
    local inc_from_lsn=$(grep "from_lsn" "$backup_path/xtrabackup_checkpoints" | awk '{print $3}')
    local last_to_lsn=$(grep "to_lsn" "$last_backup/xtrabackup_checkpoints" | awk '{print $3}')
    if [ "$inc_from_lsn" != "$last_to_lsn" ]; then
        handle_error "增量备份LSN不连续: from_lsn=$inc_from_lsn vs 前一个to_lsn=$last_to_lsn"
    fi
    
    # 创建压缩包
    log "创建压缩包: $tar_path"
    tar -zcf $tar_path -C $BACKUP_DIR/$YEAR_MONTH $backup_name
    check_status "创建压缩包失败" "创建压缩包成功"
    
    # 传输到远程服务器
    log "开始传输到远程服务器: $REMOTE_SERVER:$REMOTE_DIR"
    scp $tar_path $REMOTE_USER@$REMOTE_SERVER:$REMOTE_DIR/
    check_status "传输到远程服务器失败" "传输到远程服务器成功"
    
    # 传输成功后,删除本地压缩包,保留原始备份目录
    log "删除本地压缩包: $tar_path"
    rm -f $tar_path
}

# 主程序
log "=========================================="
log "开始备份脚本执行"

# 检查是否为每月1号或无可用备份
if [ $(date +%d) -eq 1 ] || [ -z "$(find_last_backup 2>/dev/null)" ]; then
    log "今天是每月1号 或 无可用备份,执行全量备份"
    full_backup
else
    log "执行增量备份(基于前一个备份)"
    incremental_backup
fi

log "备份脚本执行完成"
log "=========================================="

 

 

 

数据恢复

注意,数据库版本 和 XtraBackup  需要一致

# Backup恢复脚本(修复LSN匹配问题)
# 功能:正确准备备份并处理增量合并,添加LSN验证

# 配置信息
USER="root"              
PASSWORD="123456"        
BACKUP_DIR="/opt/xtrabackup/test"  
MYSQL_DATA_DIR="/var/lib/mysql"    
MYSQL_SERVICE="mysqld"              
EXTRACT_DIR="$BACKUP_DIR/extract" 

# 日志文件
LOG_FILE="/opt/xtrabackup/restore.log"

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

# 错误处理函数
handle_error() {
    log "ERROR: $1"
    echo "错误: $1" >&2
    exit 1
}

# 检查命令执行状态
check_status() {
    if [ $? -ne 0 ]; then
        handle_error "$1"
    else
        log "$2"
    fi
}

# 解压所有备份文件
extract_all_backups() {
    log "开始解压所有备份文件"
    mkdir -p "$EXTRACT_DIR"
    local all_backups=$(ls -t $BACKUP_DIR/*.tar.gz 2>/dev/null)
    if [ -z "$all_backups" ]; then
        handle_error "未找到备份压缩包"
    fi
    log "找到备份压缩包: $all_backups"
    for tar_file in $all_backups; do
        local backup_name=$(basename ${tar_file%.tar.gz})
        local extract_dir="$EXTRACT_DIR/$backup_name"
        mkdir -p "$extract_dir"
        tar -zxf "$tar_file" -C "$extract_dir" --strip-components=1
        check_status "解压 $tar_file 失败" "解压 $tar_file 成功"
    done
    log "解压完成"
}

# 查找备份并排序
find_sorted_backups() {
    log "查找并排序备份"
    local full_backups=$(ls -td $EXTRACT_DIR/xtrabackup_full_* 2>/dev/null)
    if [ -z "$full_backups" ]; then
        handle_error "未找到全量备份"
    fi
    local latest_full=$(echo $full_backups | awk '{print $1}')
    log "最新全量备份: $latest_full"
    
    # 提取完整备份的日期部分(如 20250619)
    local base_date=$(basename "$latest_full" | cut -d'_' -f3 | cut -c1-8)
    
    # 按时间戳排序增量备份(从旧到新)
    local inc_backups=$(ls -t -d $EXTRACT_DIR/xtrabackup_add_${base_date}_* 2>/dev/null | sort)
    log "找到增量备份: $inc_backups"
    
    export LATEST_FULL="$latest_full"
    export INCREMENTAL_BACKUPS="$inc_backups"
}


# 准备备份函数关键部分
prepare_backups() {
    log "开始准备备份"
    log "准备全量备份(带--apply-log-only)"
    xtrabackup --prepare --apply-log-only --target-dir=$LATEST_FULL
    check_status "全量备份准备失败" "全量备份准备成功"
    
    # 获取全量备份的to_lsn
    local full_to_lsn=$(grep "to_lsn" "$LATEST_FULL/xtrabackup_checkpoints" | awk '{print $3}')
    log "全量备份 to_lsn: $full_to_lsn"
    
    if [ -n "$INCREMENTAL_BACKUPS" ]; then
        local inc_list=($INCREMENTAL_BACKUPS)
        local last_idx=$(( ${#inc_list[@]} - 1 ))
        
        log "开始合并增量备份(共 ${#inc_list[@]} 个)"
        for i in "${!inc_list[@]}"; do
            local inc_backup=${inc_list[$i]}
            local is_last=$((i == last_idx))
            
            # 验证增量备份完整性
            log "验证增量备份 $inc_backup"
            if [ ! -f "$inc_backup/xtrabackup_checkpoints" ]; then
                handle_error "增量备份 $inc_backup 缺少 xtrabackup_checkpoints 文件"
            fi
            
            # 获取增量备份的from_lsn
            local inc_from_lsn=$(grep "from_lsn" "$inc_backup/xtrabackup_checkpoints" | awk '{print $3}')
            log "增量备份 $inc_backup from_lsn: $inc_from_lsn"
            
            # 检查LSN匹配
            if [ "$inc_from_lsn" != "$full_to_lsn" ]; then
                handle_error "LSN不匹配: 全量备份to_lsn=$full_to_lsn vs 增量from_lsn=$inc_from_lsn"
            fi
            
            log "增量备份 $inc_backup 验证通过"
            
            # 合并增量备份(显式指定--apply-log参数)
            if $is_last; then
                log "合并最后一个增量备份(终极准备,--apply-log=0)"
                xtrabackup --prepare --apply-log=0 --target-dir=$LATEST_FULL --incremental-dir=$inc_backup
            else
                log "合并增量备份(应用日志,--apply-log-only)"
                xtrabackup --prepare --apply-log-only --target-dir=$LATEST_FULL --incremental-dir=$inc_backup
            fi
            
            check_status "合并增量备份 $inc_backup 失败" "合并增量备份 $inc_backup 成功"
            
            # 更新全量备份的to_lsn为当前增量的to_lsn
            full_to_lsn=$(grep "to_lsn" "$LATEST_FULL/xtrabackup_checkpoints" | awk '{print $3}')
            log "更新后全量备份 to_lsn: $full_to_lsn"
        done
        log "增量合并完成"
    else
        log "无增量备份需要合并"
    fi
    
    # 最终检查备份状态
    local check_file="$LATEST_FULL/xtrabackup_checkpoints"
    if [ -f "$check_file" ]; then
        # 同时检查backup_type和LSN一致性
        local backup_type=$(grep "backup_type" "$check_file" | awk '{print $3}')
        local last_lsn=$(grep "last_lsn" "$check_file" | awk '{print $3}')
        local to_lsn=$(grep "to_lsn" "$check_file" | awk '{print $3}')
        
        if [ "$backup_type" = "full-prepared" ] && [ "$last_lsn" = "$to_lsn" ]; then
            log "备份准备完成,状态正常 (backup_type=$backup_type, lsn=$last_lsn)"
        else
            log "检测到备份状态异常: backup_type=$backup_type, last_lsn=$last_lsn, to_lsn=$to_lsn"
            log "尝试执行终极准备命令以修复状态..."
            
            # 尝试执行终极准备命令
            xtrabackup --prepare  --target-dir=$LATEST_FULL
            if [ $? -ne 0 ]; then
                handle_error "终极准备命令执行失败,无法修复备份状态"
            fi
            
            # 再次检查状态
            backup_type=$(grep "backup_type" "$check_file" | awk '{print $3}')
            if [ "$backup_type" = "full-prepared" ]; then
                log "状态修复成功: backup_type=$backup_type"
            else
                handle_error "终极准备命令执行后状态仍未修复,backup_type=$backup_type"
            fi
        fi
    else
        handle_error "检查点文件不存在"
    fi
}



# 恢复数据
restore_data() {
    log "开始恢复数据"
    systemctl stop $MYSQL_SERVICE
    check_status "停止MySQL失败" "停止MySQL成功"
    
    rm -rf $MYSQL_DATA_DIR/*
    check_status "清理数据目录失败" "清理数据目录成功"
    
    log "复制备份数据到MySQL"
    xtrabackup --copy-back --target-dir=$LATEST_FULL
    check_status "复制数据失败" "复制数据成功"
    
    chown -R mysql:mysql $MYSQL_DATA_DIR
    chmod -R 755 $MYSQL_DATA_DIR
    check_status "设置权限失败" "设置权限成功"
    
    systemctl start $MYSQL_SERVICE
    check_status "启动MySQL失败" "启动MySQL成功"
    log "数据恢复完成"
}

# 主程序
log "=========================================="
log "开始恢复流程"

extract_all_backups
find_sorted_backups
prepare_backups
restore_data

log "恢复完成"
log "=========================================="

 

posted @ 2025-06-18 16:32  一文搞懂  阅读(89)  评论(0)    收藏  举报