crontab定时备份失效主因是环境变量缺失和路径不完整;需用绝对路径、显式声明SHELL/PATH、重定向错误日志、谨慎使用rsync--delete、MySQL改用.my.cnf配置文件、find清理用-daystart或按文件名日期过滤。

crontab 定时执行备份脚本总不生效?检查这几点
脚本手动运行正常,但加到 crontab 就没反应,最常见原因是环境变量缺失和路径不完整。cron 启动的 shell 是最小化环境,$PATH 里往往没有 /usr/local/bin 或你自定义的 bin 目录,导致找不到 rsync、mysqldump 等命令。
- 所有命令在脚本中必须写绝对路径,比如用
/usr/bin/rsync而不是rsync -
crontab -e里建议显式声明SHELL和PATH,例如:SHELL=/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin 0 2 * * * /home/user/backup.sh
- 日志重定向不能只写
> /var/log/backup.log,要加上2>&1,否则错误信息全丢掉 - 测试时先设成每分钟执行一次:
* * * * * /path/to/script.sh >> /tmp/backup.log 2>&1,确认能跑通再调回真实周期
用 rsync 做增量备份,--delete 用错会清空目标目录
rsync 是 Linux 备份主力,但 --delete 参数极其危险:它会让目标目录“严格对齐”源目录结构,源里删了的文件,目标里也会被删。生产环境误加这个参数,等于主动触发数据丢失。
- 首次备份不要带
--delete,先用rsync -av /source/ /dest/(注意末尾斜杠)同步一次 - 后续增量可加
--delete-after,确保删除动作发生在所有复制完成之后,留出检查窗口 - 务必配合
--dry-run预演:比如rsync -avn --delete-after /data/ /backup/data/,看输出哪些文件会被删 - 目标路径写错一个字符(比如写成
/backup//data)可能触发根目录级误删,脚本里建议用变量固化路径并做存在性校验:if [ ! -d "$DEST" ]; then echo "DEST not exists"; exit 1; fi
MySQL 备份脚本里 mysqldump 报错 Access denied?权限和密码写法是关键
mysqldump 在脚本里直接写密码(如 -p123456)看似方便,但会报 Access denied for user 'root'@'localhost' —— 这不是密码错了,而是 MySQL 8.0+ 默认禁用明文传参,且 -p 后紧贴密码会被解析为选项名而非值。
- 改用配置文件方式:创建
~/.my.cnf,内容为[client] user = backup_user password = your_strong_password host = localhost
,记得chmod 600 ~/.my.cnf - 脚本中调用时省略用户密码参数:
mysqldump --single-transaction --routines db_name > /backup/db_$(date +%F).sql - 不要用 root 用户做备份,单独建个只读账号:
CREATE USER 'backup_user'@'localhost' IDENTIFIED BY 'xxx'; GRANT SELECT, LOCK TABLES, SHOW VIEW, TRIGGER ON *.* TO 'backup_user'@'localhost'; - 导出大库时加
--skip-lock-tables(配合--single-transaction)避免锁表影响线上业务
备份文件保留策略:find + mtime 容易算错天数
用 find /backup -name "*.sql" -mtime +7 -delete 想删 7 天前的文件,结果昨天的也被删了——-mtime +7 表示“修改时间大于 7*24 小时”,实际是“8 天前及更早”。而且 mtime 看的是文件内容修改时间,不是创建时间,压缩包解压后 mtime 会变。
- 用
-daystart让计算从当日零点开始,更符合人类直觉:find /backup -name "*.tar.gz" -daystart -mtime +7 -delete - 更稳妥的方式是按文件名日期过滤,比如备份文件叫
web_2024-05-20.tar.gz,用ls /backup/web_*.tar.gz | head -n -7 | xargs rm -f保留最新 7 个 - 删除前先
echo出来确认:to_delete=$(ls /backup/*.sql | head -n -3) if [ -n "$to_delete" ]; then echo "Will delete: $to_delete"; fi
- 别把清理逻辑和备份逻辑写在一个脚本里,先确保备份成功(检查
rsync返回值或文件大小),再执行清理
自动备份最脆弱的环节从来不是技术本身,而是路径硬编码、权限假设、时间理解偏差这些细节。脚本上线前,至少要在测试机上模拟断电、磁盘满、网络中断三种异常场景跑一遍。










