1、脚本说明


### 1. 核心备份功能
- 全量备份:通过`mysqldump --all-databases`备份所有数据库,文件命名为`full_YYYYMMDD_HHMM.sql.gz`(存储于`/data/backup_data/mysql_dump`),包含存储过程、触发器、事件等完整元数据。  
- **指定库备份**:单独备份`test`备份、`test2`两个数据库,文件命名为`数据库名_YYYYMMDD_HHMM.sql.gz`,与全量备份保持统一格式,便于管理。  


### 2. 防重复执行机制
- 通过锁文件`/data/scripts/mysql_backup.lock`实现进程互斥:脚本启动时检查锁文件,若存在则直接退出(避免同一时间多个备份进程冲突);脚本结束后自动删除锁文件(通过`trap`机制确保异常退出时也能清理)。  


### 3. 大表监控与清理
- 针对`test`库的`test2`表(路径`/data/mysql/data/test2`):  
  - 自动检测文件大小(转换为G单位,保留2位小数)。  
  - 若文件超过10G,执行`TRUNCATE TABLE`命令清空表数据(彻底释放磁盘空间,优于`DELETE`),并记录清理前后的大小。  
  - 清理状态(未执行/无需执行/已执行/执行失败)会同步到日志和通知中。  


### 4. 备份文件清理策略
- **当月备份**:保留当月所有日期的备份(全量+指定库),方便近期数据回溯。  
- **非当月备份**:仅保留每月1号的备份,其他日期的备份自动删除(减少长期存储占用,兼顾历史数据留存需求)。  
- 对文件名格式异常(无法提取日期)的备份文件直接删除,避免无效文件残留。  


### 5. 统一通知与状态监控
- **单次钉钉通知**:备份流程结束后发送一次通知,包含以下关键信息:  
  - 备份状态:全量备份、`test`备份、`test2`备份的各自成功/失败状态。  
  - 大表信息:`user`的大小(G单位)及清理操作结果。  
  - 磁盘使用:仅展示根目录(`/`)和`/data`目录的磁盘使用率(文件系统、类型、总容量、已用/可用空间)。  
  - 保留规则:明确“当月全保留,次月仅留1号”的策略。  
- 通知包含安全关键词(如“备份”),确保通过钉钉机器人验证。  


### 6. 日志记录
- 所有操作(备份开始/结束、成功/失败详情、清理过程、大表检查结果等)均记录到`/data/backup_data/mysql_backup.log`,便于问题追溯和审计。  


### 适用场景
适用于需要定期备份MySQL数据库、监控大表占用空间、控制备份存储成本的场景,通过自动化流程减少人工干预,同时通过钉钉通知实时掌握备份状态,保障数据安全性。

2、需要添加

  • 钉钉toekn

  • mysql库名

  • mysqldump路径

#!/bin/bash

# 配置参数
BACKUPDIR='/data/backup_data/mysql_dump'
LOGFILE='/data/backup_data/mysql_backup.log'
CHECKFILE='/data/backup_data/mysql_backup_check.log'
DATABASES=("test2" "test1")  # 单个数据库列表
BACKUPDATE=$(date +%Y%m%d_%H%M)
MYSQLDUMP_PATH='/data/mysql/bin/mysqldump'#mysqldump绝对路径

LOCK_FILE="/data/scripts/mysql_backup.lock"  # 锁文件路径
# 钉钉机器人配置
DINGTALK_WEBHOOK="https://oapi.dingtalk.com/robot/send?access_token=填写token"
KEYWORD="备份"  
# 大表文件路径
TABLE_IBD_FILE="/data/mysql/data/test2/user"
# 状态变量
FULL_STATUS="失败"
declare -A DB_STATUS  # 单个库备份状态
for db in "${DATABASES[@]}"; do
    DB_STATUS[$db]="失败"
done
TABLE_SIZE_GB="0"  # 表文件大小(G)
TABLE_CLEAR_STATUS="未执行"  # 表清理状态


# 防重复执行
if [ -f "$LOCK_FILE" ]; then
    echo "[$(date)] 备份进程已在运行,退出" >> $LOGFILE
    exit 1
fi
trap 'rm -f "$LOCK_FILE"' EXIT
touch "$LOCK_FILE"

# 确保备份目录存在
mkdir -p "$BACKUPDIR" || {
    echo "[$(date)] 备份目录创建失败:$BACKUPDIR" >> $LOGFILE
    exit 1
}

# 初始化检查文件
echo "false" > "$CHECKFILE"

# 开始日志
echo "=====================================" >> $LOGFILE
echo "Backup started at $(date)" >> $LOGFILE


# 1. 检查并清理大表(超过10G则清空)
echo "[$(date)] 开始检查大表文件: $TABLE_IBD_FILE" >> $LOGFILE
if [ -f "$TABLE_IBD_FILE" ]; then
    # 获取文件大小(字节)并转换为G
    FILE_SIZE_BYTES=$(stat -c %s "$TABLE_IBD_FILE")
    TABLE_SIZE_GB=$(echo "scale=2; $FILE_SIZE_BYTES / 1073741824" | bc)  # 1G=1024^3字节
    
    # 判断是否超过10G
    if (( $(echo "$TABLE_SIZE_GB > 10" | bc -l) )); then
        echo "[$(date)] 表文件超过10G(当前${TABLE_SIZE_GB}G),执行清空操作" >> $LOGFILE
        # 执行清空表SQL(TRUNCATE会释放空间,比DELETE更彻底)
        /data/mysql/bin/mysql -uroot -padmin -h192.168.1.10 -P3306 -e "USE test2; TRUNCATE TABLE user;" 2>> $LOGFILE
        
        if [ $? -eq 0 ]; then
            echo "[$(date)] 表gen_stm_mutu_rcrd清空成功" >> $LOGFILE
            TABLE_CLEAR_STATUS="已执行(原大小${TABLE_SIZE_GB}G)"
            # 重新获取清理后的大小
            NEW_SIZE_BYTES=$(stat -c %s "$TABLE_IBD_FILE")
            NEW_SIZE_GB=$(echo "scale=2; $NEW_SIZE_BYTES / 1073741824" | bc)
            TABLE_SIZE_GB="清理后: ${NEW_SIZE_GB}G"
        else
            echo "[$(date)] 表gen_stm_mutu_rcrd清空失败" >> $LOGFILE
            TABLE_CLEAR_STATUS="执行失败(原大小${TABLE_SIZE_GB}G)"
        fi
    else
        echo "[$(date)] 表文件大小正常(${TABLE_SIZE_GB}G),无需清理" >> $LOGFILE
        TABLE_CLEAR_STATUS="无需执行(当前${TABLE_SIZE_GB}G)"
    fi
else
    echo "[$(date)] 表文件不存在: $TABLE_IBD_FILE" >> $LOGFILE
    TABLE_SIZE_GB="文件不存在"
    TABLE_CLEAR_STATUS="未执行"
fi


# 2. 全量备份
echo "[$(date)] 开始全量数据库备份" >> $LOGFILE
FULL_BACKUP_FILE="${BACKUPDIR}/full_${BACKUPDATE}.sql.gz"
"$MYSQLDUMP_PATH" --single-transaction -uroot -padmin -h192.168.1.10 -P3306 \
    --opt --master-data=2 --hex-blob --triggers --routines --events --force --all-databases \
    | gzip > "$FULL_BACKUP_FILE" 2>> $LOGFILE

if [ $? -eq 0 ]; then
    echo "[$(date)] 全量备份成功" >> $LOGFILE
    FULL_STATUS="成功"
else
    echo "[$(date)] 全量备份失败" >> $LOGFILE
    FULL_STATUS="失败"
fi


# 3. 单个库备份
for DATABASE_NAME in "${DATABASES[@]}"; do
    echo "[$(date)] 开始备份数据库 $DATABASE_NAME" >> $LOGFILE
    "$MYSQLDUMP_PATH"  --single-transaction -uroot -padmin -h192.168.1.10 -P3306 \
        --opt --master-data=2 --hex-blob --triggers --routines --events --force \
        "$DATABASE_NAME" | gzip > "${BACKUPDIR}/${DATABASE_NAME}_${BACKUPDATE}.sql.gz" 2>> $LOGFILE

    if [ $? -eq 0 ]; then
        echo "[$(date)] $DATABASE_NAME 备份成功" >> $LOGFILE
        DB_STATUS[$DATABASE_NAME]="成功"
    else
        echo "[$(date)] $DATABASE_NAME 备份失败" >> $LOGFILE
        DB_STATUS[$DATABASE_NAME]="失败"
    fi
done


# 4. 清理逻辑:当月每日备份全保留,次月仅保留1号备份
echo "[$(date)] 开始执行备份清理(当月全保留,次月仅留1号)" >> $LOGFILE
CURRENT_YM=$(date +%Y%m)  # 当前年月(格式:202510)

for backup_file in "$BACKUPDIR"/*.sql.gz; do
    [ -f "$backup_file" ] || continue  # 跳过非文件
    
    # 从文件名提取日期(格式YYYYMMDD)
    date_str=$(basename "$backup_file" | grep -oE '[0-9]{8}')
    if [ -z "$date_str" ]; then
        echo "[$(date)] 删除格式异常文件: $backup_file" >> $LOGFILE
        rm -f "$backup_file"
        continue
    fi
    
    # 提取文件的年月(YYYYMM)和日(DD)
    FILE_YM=${date_str:0:6}  # 取前6位(例如20251005→202510)
    day=$(echo "$date_str" | cut -c7-8)  # 日(DD)
    
    # 判断逻辑:
    # 1. 当月备份(文件年月=当前年月)→ 全保留
    # 2. 非当月备份(文件年月<当前年月)→ 仅保留1号,其他删除
    if [ "$FILE_YM" -eq "$CURRENT_YM" ]; then
        echo "[$(date)] 当月备份,保留: $backup_file" >> $LOGFILE
    else
        if [ "$day" = "01" ]; then
            echo "[$(date)] 非当月1号备份,保留: $backup_file" >> $LOGFILE
        else
            echo "[$(date)] 非当月非1号备份,删除: $backup_file" >> $LOGFILE
            rm -f "$backup_file"
        fi
    fi
done
echo "[$(date)] 清理完成" >> $LOGFILE


# 5. 整体状态判断
ALL_SUCCESS=true
[ "$FULL_STATUS" != "成功" ] && ALL_SUCCESS=false
for db in "${DATABASES[@]}"; do
    [ "${DB_STATUS[$db]}" != "成功" ] && ALL_SUCCESS=false && break
done
[ "$ALL_SUCCESS" = true ] && echo "true" > "$CHECKFILE" || echo "false" > "$CHECKFILE"

echo "Backup process completed at $(date)" >> $LOGFILE


# 6. 准备钉钉通知内容
# 获取根目录和/data目录的磁盘信息
DF_INFO=$(df -hT | grep -E '^/dev/' | grep -E '/$|/data$' | awk '{printf "文件系统: %s  类型: %s  总容量: %s  已用: %s  可用: %s  挂载点: %s\n", $1, $2, $3, $4, $5, $6}')

# 构建通知内容
NOTIFY_TITLE="❌ ${KEYWORD}异常"
[ "$ALL_SUCCESS" = true ] && NOTIFY_TITLE="✅ ${KEYWORD}成功"

NOTIFY_CONTENT="${NOTIFY_TITLE}
时间: $(date +'%Y-%m-%d %H:%M:%S')
备份状态:
- 全量备份(all): ${FULL_STATUS}
- xxljob备份: ${DB_STATUS[xxljob]}
- hwms_wallet备份: ${DB_STATUS[hwms_wallet]}

大表信息:
- 文件: user
- 大小: ${TABLE_SIZE_GB}
- 清理操作: ${TABLE_CLEAR_STATUS}

磁盘使用情况:
$DF_INFO

保留规则: 当月每日备份全保留,次月仅保留1号备份"


# 发送钉钉通知(仅一次)
curl -s -H "Content-Type: application/json" \
     -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"$NOTIFY_CONTENT\"}}" \
     "$DINGTALK_WEBHOOK" >> $LOGFILE 2>&1
echo "[$(date)] 钉钉通知已发送(整体状态:$(cat "$CHECKFILE"))" >> $LOGFILE