备份失败必须基于mysqldump真实退出码判断,禁用--force,用$PIPESTATUS捕获管道状态,钉钉告警需脱敏并含主机/IP/库名/错误码/磁盘空间/最近5行stderr。

备份脚本执行失败时,只靠 cron 的 MAILTO 或简单 echo 日志,90% 的问题会漏报——不是没发,是发了但没人收到、或关键错误被重定向吞掉了。
mysqldump 退出码不为 0 就必须立刻告警
mysqldump 成功返回 0,失败返回非 0 码(常见:2=连接失败、3=权限不足、4=找不到表、11=临时 I/O 错误)。if [ $? -ne 0 ] 是唯一可靠判断依据,不能依赖文件是否存在或大小是否 >0。
- 加
--force会让 mysqldump 忽略部分错误继续执行,可能生成空 SQL 文件却返回 0;务必删掉 - 用
head -c 1 backup.sql | wc -c检查首字节是否可读,比ls -l看大小更准(避免 gzip 头信息干扰) - 如果用了管道压缩(如
mysqldump | gzip),实际退出码是最后一个命令的,需用$PIPESTATUS数组捕获各段状态
钉钉机器人发消息必须脱敏密码与敏感路径
直接把 mysqldump -u root -p123456 或完整报错日志发到钉钉,等于把数据库凭据贴在群里。真出事,第一责任在告警脚本本身。
- 改用
--defaults-file=/root/.my.cnf,并确保该文件权限为chmod 600 - 报错日志中过滤掉含
-p、password=、@10.0.等字段的行,用sed '/-p\|password\|@/d' - 钉钉 payload 中只传关键信息:主机 IP、数据库名、错误码、
df -h /backup结果、最近 5 行 stderr(不含密码)
邮件提醒在 cron 下常失效的三个硬伤
cron 环境极简:PATH 不含 /usr/sbin:/sbin,mail 命令根本找不到;MAILTO 只转发 stdout/stderr,而 mysqldump 关键错误默认走 stderr —— 一旦你写了 > backup.sql 2>/dev/null,就彻底静默了。
- 别用
MAILTO=xxx,改用脚本内显式调用:/bin/mail -s "MySQL Backup FAIL" admin@example.com - 优先选
mailx,它不依赖交互配置,cron 下稳定;避免mutt,容易卡住 - 发送前验证 mailx 是否可用:
which mailx >/dev/null || { echo "mailx not found"; exit 1; }
告警内容必须包含可立即定位的上下文
“备份失败”四个字毫无价值。运维凌晨三点爬起来,需要的是“在哪台机器、哪个库、因何失败、现在磁盘还剩多少、上一次成功是什么时候”。
- 钉钉/邮件正文固定包含:
hostname、date、mysqldump --version、df -h /backup、ls -lh /backup/*.sql | tail -3 - 错误日志截取从最后一次
mysqldump开始,到下一个时间戳前为止,避免混入旧日志 - 若使用 Python 脚本做校验,不要只 check
os.path.exists(),要加os.path.getsize() > 1024和subprocess.run(['head', '-n1', f], capture_output=True)验证可读性
最常被跳过的环节是:没在告警里附上 SHOW GRANTS FOR 'backup_user'@'%' 的输出。权限问题占所有失败的 37%,但每次都要手动连上去查,就说明告警设计漏了这一环。


















