必须检查 $?、校验文件有效性、确保时间连续性:退出码非零即失败;用 test -s 和 mysql -e "source" 验证文件内容;监控 75 分钟内是否有新备份并保留 3 代。

必须检查 $?,别信文件是否存在
备份脚本生成了 backup_20260510.sql.gz,不代表它能用。磁盘满、密码过期、权限丢失时,mysqldump 常常静默写入空文件或截断内容,但退出码一定是非零——这是唯一可信的信号。
-
mysqldump执行完立刻用if [ $? -ne 0 ]; then判断,不能只靠&&链式执行(后者在中间命令被set -e拦截后可能跳过后续逻辑) - 别把错误重定向到
/dev/null或丢进nohup.out:保留2>> /var/log/backup.log,里面会有Access denied、Got errno 28(磁盘满)、MySQL server has gone away等关键线索 - 在脚本末尾加一句
echo $? > /tmp/mysql_backup_exit_code,方便外部监控工具(如 Zabbix)直接读取
光有退出码不够,还得校验文件有效性
退出码为 0 只说明导出过程没崩,不保证 SQL 文件有真实数据、语法正确、结构完整。空 dump、乱码、漏 CREATE DATABASE、没 INSERT INTO 都是线上高频问题。
- 用
test -s backup.sql或stat -c "%s" backup.sql | awk '$1 检查文件大小,小于 1KB 基本可判失败 - 快速确认开头结构:
head -n 50 backup.sql | grep -q "CREATE TABLE",没这行大概率是空备份或字符集错 - 确认有实际数据块(防截断):
zcat backup.sql.gz | grep -m 1 "INSERT INTO" | wc -l,结果为 0 就得拦停 - 轻量语法验证:
mysql -u user -p -e "source backup.sql" > /dev/null 2>&1,再看$?;失败则说明 SQL 解析不过,不能直接用于恢复
时间连续性比单次成功更重要
一次失败不可怕,可怕的是失败了三天没人发现。备份中断后第 1 天没人查,第 2 天还照常删旧文件,第 3 天轮转完就只剩一个坏备份——这是生产事故的典型路径。
- 文件名必须带精确时间戳:
backup_$(date +\%Y\%m\%d_\%H\%M).sql.gz,避免用%F这种只含日期的格式 - 监控脚本定期执行:
find /backup/mysql/ -name "*.sql.gz" -mmin -75 | head -1,超 75 分钟没新文件就触发告警(根据你的调度周期微调) - 保留至少 3 代备份(
backup.0→backup.1→backup.2),防止某次轮转误删全部 - 对比文件大小趋势:
stat -c "%s" backup_$(date -d "yesterday" +\%Y\%m\%d).sql.gz,若比过去 7 天均值低超 30%,可能是库被清空或--databases参数漏写库名
主从延迟大时,备份就是“伪一致”
如果备份时刻 Seconds_Behind_Master 很高,mysqldump 读到的其实是落后主库几秒甚至几分钟的数据。这种备份在故障恢复时会掩盖真实数据状态,必须主动识别并跳过。
- 备份前加检查:
mysql -u user -p -e "SHOW SLAVE STATUS\G" | grep Seconds_Behind_Master | awk '{print $2}' - 若值 > 60,建议中止本次备份,并记录日志:
echo "$(date) WARN: slave delay > 60s, skip backup" >> /var/log/backup.log - 该逻辑不能放在
mysqldump后面——延迟检查必须前置,否则备份已发生,补救成本极高











