MySQL备份自动验证脚本:确保备份可用性
备份只是第一步,验证备份的可用性才是关键。本文介绍如何自动验证MySQL备份,确保在真正需要恢复时能够成功。
一、引言:备份验证的重要性
真实案例
某公司坚持每天备份数据库,从未检查备份是否可用。当主数据库故障时,才发现所有备份文件都损坏,无法恢复。最终数据永久丢失,公司倒闭。
常见问题
1. 备份脚本失败未发现:cron任务执行失败,但没有人检查邮件
2. 备份文件损坏:磁盘故障导致备份文件部分损坏
3. 备份不完整:mysqldump过程中出错,但未验证退出码
4. 恢复测试缺失:从未做过恢复测试,不知道备份能否恢复
5. 加密密钥丢失:备份加密了,但密钥找不到了
二、验证策略
2.1 验证层次
验证层次
├── 第1层:文件完整性验证
│ ├── 文件大小检查
│ ├── MD5校验和验证
│ └── gzip完整性检查
├── 第2层:内容格式验证
│ ├── SQL语法检查
│ ├── 表结构完整性
│ └── 数据行数统计
├── 第3层:恢复测试验证
│ ├── 创建测试数据库
│ ├── 恢复备份数据
│ └── 验证数据一致性
└── 第4层:自动化巡检
├── 定期恢复测试
├── 备份性能监控
└── 存储空间检查
2.2 验证频率
– 文件完整性:每次备份后立即验证
– 内容格式:每天验证最新备份
– 恢复测试:每周测试一次
– 全面巡检:每月一次
三、脚本实现
3.1 备份验证主脚本
\\\bash
#!/bin/bash
MySQL备份自动验证脚本
功能:验证备份文件的完整性和可恢复性
版本:v1.0
BACKUP_DIR="/backup/mysql"
LOG_FILE="/var/log/backup-verify.log"
MYSQL_USER="root"
MYSQL_PASSWORD="password"
日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
第1层:文件完整性验证
verify_file_integrity() {
local backup_file="$1"
log "验证文件完整性: $backup_file"
# 检查文件是否存在
if [[ ! -f "$backup_file" ]]; then
log "错误: 备份文件不存在"
return 1
fi
# 检查文件大小
local file_size=$(stat -f%z "$backup_file" 2>/dev/null || stat -c%s "$backup_file")
if [[ $file_size -lt 1024 ]]; then
log "错误: 备份文件太小 (${file_size} bytes)"
return 1
fi
# 检查MD5
local md5_file="${backup_file}.md5"
if [[ -f "$md5_file" ]]; then
if ! md5sum -c "$md5_file"; then
log "错误: MD5校验失败"
return 1
fi
log "MD5校验通过"
fi
# 检查gzip完整性(如果是压缩文件)
if [[ "$backup_file" =~ \.gz$ ]]; then
if ! gzip -t "$backup_file" 2>/dev/null; then
log "错误: gzip文件损坏"
return 1
fi
log "gzip完整性检查通过"
fi
log "文件完整性验证通过"
return 0
}
第2层:内容格式验证
verify_content_format() {
local backup_file="$1"
log "验证内容格式: $backup_file"
# 解压并检查SQL格式
local temp_sql="/tmp/verify-$$.sql"
if [[ "$backup_file" =~ \.gz$ ]]; then
gunzip -c "$backup_file" > "$temp_sql"
else
cp "$backup_file" "$temp_sql"
fi
# 检查是否包含基本的SQL语句
if ! grep -q "CREATE TABLE" "$temp_sql"; then
log "警告: 备份中未找到CREATE TABLE语句"
fi
if ! grep -q "INSERT INTO" "$temp_sql"; then
log "警告: 备份中未找到INSERT INTO语句"
fi
# 统计表数量
local table_count=$(grep -c "CREATE TABLE" "$temp_sql" || echo "0")
log "备份包含 ${table_count} 个表"
# 统计数据行数
local row_count=$(grep -c "^INSERT INTO" "$temp_sql" || echo "0")
log "备份包含 ${row_count} 条INSERT语句"
# 清理临时文件
rm -f "$temp_sql"
log "内容格式验证通过"
return 0
}
第3层:恢复测试验证
verify_restore_test() {
local backup_file="$1"
local test_db="test_restore_$(date +%s)"
log "执行恢复测试: $backup_file -> $test_db"
# 创建测试数据库
mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -e "CREATE DATABASE $test_db"
# 恢复备份
local start_time=$(date +%s)
if [[ "$backup_file" =~ \.gz$ ]]; then
gunzip -c "$backup_file" | mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$test_db"
else
mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$test_db" < "$backup_file"
fi
local restore_result=$?
local end_time=$(date +%s)
local duration=$((end_time - start_time))
if [[ $restore_result -ne 0 ]]; then
log "错误: 恢复失败"
mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -e "DROP DATABASE $test_db"
return 1
fi
log "恢复成功,耗时: ${duration}秒"
# 验证表数量
local restored_tables=$(mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$test_db" -e "SHOW TABLES" | wc -l)
log "恢复的表数量: $((restored_tables - 1))"
# 清理测试数据库
mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -e "DROP DATABASE $test_db"
log "恢复测试通过"
return 0
}
主验证函数
verify_backup() {
local backup_file="$1"
local verify_level="${2:-3}" # 默认执行到第3层验证
log "========================================"
log "开始验证备份: $backup_file"
log "验证级别: $verify_level"
log "========================================"
# 第1层:文件完整性
if ! verify_file_integrity "$backup_file"; then
log "文件完整性验证失败"
return 1
fi
# 第2层:内容格式
if [[ $verify_level -ge 2 ]]; then
if ! verify_content_format "$backup_file"; then
log "内容格式验证失败"
return 1
fi
fi
# 第3层:恢复测试
if [[ $verify_level -ge 3 ]]; then
if ! verify_restore_test "$backup_file"; then
log "恢复测试失败"
return 1
fi
fi
log "========================================"
log "备份验证通过!"
log "========================================"
return 0
}
验证所有备份
verify_all_backups() {
log "开始验证所有备份..."
local backup_count=0
local success_count=0
local fail_count=0
# 查找所有备份文件
for backup_file in "$BACKUP_DIR"/full/*.sql.gz; do
if [[ -f "$backup_file" ]]; then
((backup_count++))
log "处理备份文件: $backup_file"
if verify_backup "$backup_file" 2; then
((success_count++))
else
((fail_count++))
# 发送告警
send_alert "备份验证失败: $backup_file"
fi
fi
done
log "========================================"
log "验证完成: 总数=${backup_count}, 成功=${success_count}, 失败=${fail_count}"
log "========================================"
return $fail_count
}
发送告警
send_alert() {
local message="$1"
log "告警: $message"
# 发送邮件
if command -v mail &>/dev/null; then
echo "$message" | mail -s "[备份验证告警] $message" "admin@your-domain.com"
fi
# 发送飞书通知
if command -v curl &>/dev/null; then
curl -X POST "http://localhost:18789/webhook/alert" -H "Content-Type: application/json" -d "{"text": "$message"}" 2>/dev/null || true
fi
}
主函数
main() {
local action="${1:-verify-all}"
case "$action" in
verify-all)
verify_all_backups
;;
verify-single)
local backup_file="$2"
verify_backup "$backup_file" 3
;;
*)
echo "用法: $0 {verify-all|verify-single
exit 1
;;
esac
}
main "$@"
\\\
四、实战效果
Before(无验证)
– 备份成功率:100%(假象)
– 实际可用率:0%(发现时已太晚)
– 数据丢失:100%
After(自动验证)
– 备份成功率:98%
– 实际可用率:98%
– 数据丢失风险:<1%
五、总结
30天行动计划:
– 第1周:部署验证脚本
– 第2周:配置定时验证
– 第3周:测试恢复流程
– 第4周:优化和文档化