可靠备份清理需三原则:一、依据文件名日期而非mtime判断过期,如解析“mysql_prod_20260810_020001.sql.gz”中的20260810;二、用mv隔离再删,配合独立日志与低权限用户backupclean运行;三、删除前校验文件有效性,抽查内容与大小,失败则告警并移至/bad/目录。

清理脚本不能只依赖 find -mtime
直接用 find /backup -name "*.sql.gz" -mtime +7 -delete 看似简单,但实际会误删:备份中途失败、重试写入的文件,mtime 被更新过,导致刚生成几小时的备份被当成“7天前”删掉;勒索软件篡改系统时间后,mtime 更不可信。
真正可靠的依据是文件名里的日期。比如备份文件叫 mysql_prod_20260810_020001.sql.gz,就该用 date -d "20260810" +%s 解析出时间戳比对。
- 命名必须统一且含完整日期时间,推荐格式:
dbname_YYYYMMDD_HHMMSS.sql.gz - 脚本里用
basename提取文件名,再用cut或正则提取前8位日期 - 计算保留截止时间戳:
keep_until=$(date -d "$(date +%Y%m%d) -15 days" +%s 2>/dev/null) - 对每个文件做
[ "$file_ts" -lt "$keep_until" ]判断,而非依赖-mtime
rm -f 不是安全删除方式
线上环境执行清理时,rm -f 一旦路径写错或变量为空,就是无差别清空——比如 rm -f $BACKUP_DIR/* 中 $BACKUP_DIR 未定义,就会删掉当前目录下所有文件。
更稳妥的做法是先隔离再确认:
- 用
mv "$f" /backup/trash/把待删文件移到隔离区,而不是直接删 - 隔离区挂载为单独分区或设置
chattr +a防止覆盖写入 - 24 小时后运行二次脚本检查
/backup/trash/里文件是否真实过期,再执行rm -f - 所有清理操作必须记录到独立日志,包含文件名、原始路径、移动时间、操作用户
清理任务必须降权运行
如果清理脚本用 root 身份跑,crontab 被植入恶意规则后,攻击者就能借清理流程删光所有备份。这不是假设,是已知攻击链。
正确做法是创建专用低权限用户:
- 新建用户:
useradd -r -s /bin/bash backupclean - 仅赋予备份目录的
r-x权限:chmod 750 /backup && chown :backupclean /backup - 清理脚本属主设为
backupclean:backupclean,禁止写权限 - crontab 只从
/etc/cron.d/加载,文件权限必须是0644,属主root:root
删之前必须校验备份有效性
很多备份脚本跑完就认为成功了,其实 mysqldump 因锁表失败、网络中断或磁盘满退出时,返回码仍是 0(尤其 MySQL 5.7 及更早版本),生成的是空文件或截断内容。
清理前至少做两层检查:
- 解压并读头 10 行:
zcat "$f" 2>/dev/null | head -n 10 | grep -q "MySQL dump" - 检查文件大小是否超过 1KB:
[ $(stat -c %s "$f" 2>/dev/null) -gt 1024 ] - 若任一检查失败,跳过删除,并发邮件告警,同时把文件移到
/backup/bad/目录留证 - 不要在清理脚本里调用
mysqldump --help或其他可能被注入的命令
find 命令,而在于让清理行为可审计、可回退、可验证——时间戳从文件名来,删除动作要隔离,执行权限要收缩,文件内容要抽查。漏掉任何一环,自动化就变成自动毁灭。


















