Linux crontab定时任务不执行常见原因与排查
前言
crontab 写好了却到点不执行,是服务器运维最常见的坑之一。手动跑脚本正常,定时任务却没反应,问题往往不在脚本本身,而在服务状态、环境变量、时间格式和权限。本篇按从易到难的顺序列出排查点。
一、先确认crond服务在运行
systemctl status crond
# 没启动就启动并设开机自启
systemctl start crond
systemctl enable crond
服务没跑,所有定时任务都不会触发,这是第一个要查的。
二、检查定时任务格式是否正确
标准格式:分 时 日 月 周 命令
# 查看当前用户的定时任务
crontab -l
# 例如:每10分钟执行一次脚本
*/10 * * * * /usr/bin/php /www/backup.php
常见格式错误:分钟位置写成了小时、用了 * 却想表示"第1个"、命令路径用了相对路径。
三、查看cron日志,判断有没有触发
# CentOS7 日志位置
tail -f /var/log/cron
到点看日志里有没有出现这条任务:
- 日志里根本没有:格式没被识别、服务没跑、或时间写错;
- 日志有执行记录但没效果:是脚本/环境问题,往下查。
四、脚本环境变量问题(高频)
cron 的环境比手动登录干净得多,很多命令找不到、相对路径失败。解决:
- 用绝对路径:脚本里和任务里都写全,比如
/usr/bin/php、/www/backup.php; - 开头加载环境:
*/10 * * * * source /etc/profile; /usr/bin/php /www/backup.php - 先手动用cron环境复现:
env -i /bin/bash script.sh模拟干净环境跑一遍。
五、权限与重定向
- 脚本要有执行权限:
chmod +x /www/backup.sh; - 脚本没加 shebang(
#!/bin/bash)会找不到解释器; - 把错误输出留下来方便定位:
*/10 * * * * /usr/bin/php /www/backup.php >> /www/log/cron.log 2>&1加了
2>&1,报错就写进日志,不再"静默失败"。
六、其他易漏点
- 时区不一致:
timedatectl看系统时区,容器里时间错会导致任务错位; - 日和周同时指定是"或"关系,容易误触发,注意语义;
- root和普通用户任务混淆:
sudo crontab -l看root的,普通用户看自己的; - 磁盘满了:日志写不进去,任务看似没执行。
总结
排查顺序:crond服务是否运行 → 任务格式对不对 → /var/log/cron 有没有触发 → 绝对路径和环境变量 → 执行权限与错误重定向 → 时区。手动正常、定时不跑,九成是环境变量和绝对路径问题。
遇到问题加QQ23979811 协助处理
MySQL定时备份脚本,自动删除旧备份
前言
数据库数据很重要,必须每天自动备份。写个备份脚本,自动备份+自动删旧的,不用手动管。
一、备份脚本
完整脚本 backup_mysql.sh
#!/bin/bash
# MySQL配置
DB_USER="root"
DB_PASS="your_password"
DB_NAME="mydb"
# 备份目录
BACKUP_DIR="/data/backup/mysql"
# 保留天数
KEEP_DAYS=7
# 日期
DATE=$(date +%Y%m%d_%H%M%S)
# 备份文件名
BACKUP_FILE="$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz"
# 创建目录
mkdir -p $BACKUP_DIR
# 备份并压缩
mysqldump -u$DB_USER -p$DB_PASS $DB_NAME | gzip > $BACKUP_FILE
# 检查备份是否成功
if [ $? -eq 0 ]; then
echo "[$(date)] 备份成功: $BACKUP_FILE"
else
echo "[$(date)] 备份失败!"
exit 1
fi
# 删除7天前的旧备份
find $BACKUP_DIR -name "*.sql.gz" -mtime +$KEEP_DAYS -delete
echo "[$(date)] 旧备份清理完成"
二、脚本说明
各部分解释
| 部分 | 说明 |
|---|---|
| DB_USER | 数据库用户名 |
| DB_PASS | 数据库密码 |
| DB_NAME | 要备份的数据库名 |
| BACKUP_DIR | 备份存放目录 |
| KEEP_DAYS | 保留几天的备份 |
| mysqldump | 备份命令 |
| gzip | 压缩,节省空间 |
| find -mtime +7 | 找7天前的文件删除 |
备份全部数据库
mysqldump -u$DB_USER -p$DB_PASS --all-databases | gzip > $BACKUP_FILE
备份多个数据库
mysqldump -u$DB_USER -p$DB_PASS --databases db1 db2 db3 | gzip > $BACKUP_FILE
三、设置执行权限
chmod +x /data/backup/backup_mysql.sh
四、测试脚本
/data/backup/backup_mysql.sh
看有没有备份文件生成:
ls -lh /data/backup/mysql/
五、设置定时任务
每天凌晨3点备份
crontab -e
添加:
0 3 * * * /data/backup/backup_mysql.sh >> /data/backup/backup.log 2>&1
说明:
0 3 * * *每天凌晨3点>> backup.log输出写到日志2>&1错误也写进去
查看定时任务
crontab -l
六、备份文件恢复
恢复步骤
# 解压
gunzip < mydb_20261008_030000.sql.gz | mysql -u root -p mydb
或者先解压再恢复
# 解压
gunzip mydb_20261008_030000.sql.gz
# 恢复
mysql -u root -p mydb < mydb_20261008_030000.sql
七、更完善的脚本
带日志和邮件通知的版本
#!/bin/bash
DB_USER="root"
DB_PASS="your_password"
DB_NAME="mydb"
BACKUP_DIR="/data/backup/mysql"
KEEP_DAYS=7
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz"
LOG_FILE="$BACKUP_DIR/backup.log"
mkdir -p $BACKUP_DIR
echo "========== $(date) 开始备份 ==========" >> $LOG_FILE
# 备份
mysqldump -u$DB_USER -p$DB_PASS $DB_NAME 2>>$LOG_FILE | gzip > $BACKUP_FILE
if [ $? -eq 0 ]; then
FILE_SIZE=$(ls -lh $BACKUP_FILE | awk '{print $5}')
echo "[$(date)] 备份成功,文件大小: $FILE_SIZE" >> $LOG_FILE
else
echo "[$(date)] 备份失败!" >> $LOG_FILE
# 这里可以加邮件通知
exit 1
fi
# 删除旧备份
find $BACKUP_DIR -name "*.sql.gz" -mtime +$KEEP_DAYS -delete
echo "[$(date)] 清理 $KEEP_DAYS 天前的旧备份完成" >> $LOG_FILE
echo "========== $(date) 备份结束 ==========" >> $LOG_FILE
常见坑
坑1:密码写在脚本里不安全
脚本里明文写了数据库密码。
解决: 用mysql配置文件:
# /root/.my.cnf
[client]
user=root
password=your_password
然后脚本里就不用写密码了:
mysqldump $DB_NAME | gzip > $BACKUP_FILE
设置权限:
chmod 600 /root/.my.cnf
坑2:备份文件太大
不压缩的话备份文件很大。
解决: 用gzip压缩,能压缩到原来的1/10。
坑3:定时任务不执行
cron里写了脚本,但没执行。
原因:
- 脚本没加执行权限
- 路径写错了
- 环境变量不对
解决: 脚本里用全路径,比如 /usr/bin/mysqldump。
坑4:备份完没验证
备份文件损坏了都不知道。
解决: 定期找个测试库恢复一下,看备份能不能用。
坑5:备份文件存在同一台服务器
服务器挂了,备份也没了。
解决: 把备份传到远程服务器或OSS。
# 传到远程服务器
scp $BACKUP_FILE root@192.168.1.20:/data/backup/
总结
备份三步走:
- 写备份脚本(mysqldump + gzip + find删旧)
- 加执行权限
- 加crontab定时任务
记住: 备份不是备份了就完了,要定期验证能不能恢复!
遇到问题加QQ23979811 协助处理
MySQL备份数据库,mysqldump定时备份
前言
数据库不备份,出事就完蛋。本文讲用mysqldump备份MySQL,再配crontab定时自动备份。
一、mysqldump基本用法
备份单个数据库
mysqldump -u root -p mydb > /backup/mydb.sql
输入密码后,就把mydb库备份到mydb.sql了。
备份多个数据库
mysqldump -u root -p --databases db1 db2 > /backup/mydbs.sql
备份所有数据库
mysqldump -u root -p --all-databases > /backup/all.sql
备份单个表
mysqldump -u root -p mydb users > /backup/users.sql
二、常用参数
| 参数 | 说明 |
|---|---|
--single-transaction |
InnoDB一致性备份,不锁表 |
--routines |
备份存储过程和函数 |
--triggers |
备份触发器 |
--events |
备份事件调度器 |
--master-data=2 |
记录binlog位置,用于主从 |
--default-character-set=utf8mb4 |
指定字符集 |
推荐完整命令
mysqldump -u root -p \
--single-transaction \
--routines \
--triggers \
--events \
--default-character-set=utf8mb4 \
mydb > /backup/mydb_$(date +%Y%m%d).sql
三、压缩备份
备份完太大,直接压缩:
mysqldump -u root -p mydb | gzip > /backup/mydb.sql.gz
恢复的时候:
gunzip < /backup/mydb.sql.gz | mysql -u root -p mydb
四、定时备份脚本
创建备份脚本:
vi /usr/local/bin/mysql_backup.sh
内容:
#!/bin/bash
# 备份目录
BACKUP_DIR=/backup/mysql
# 数据库配置
DB_USER=root
DB_PASS=你的密码
DB_NAME=mydb
# 创建备份目录
mkdir -p $BACKUP_DIR
# 备份文件名(带日期)
DATE=$(date +%Y%m%d_%H%M%S)
FILE=$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz
# 执行备份
mysqldump -u$DB_USER -p$DB_PASS \
--single-transaction \
--routines \
--triggers \
--default-character-set=utf8mb4 \
$DB_NAME | gzip > $FILE
# 删除7天前的备份
find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete
echo "备份完成:$FILE"
加执行权限:
chmod +x /usr/local/bin/mysql_backup.sh
五、配置crontab定时任务
编辑crontab:
crontab -e
加上:
# 每天凌晨2点备份
0 2 * * * /usr/local/bin/mysql_backup.sh >> /var/log/mysql_backup.log 2>&1
六、恢复备份
恢复普通备份
mysql -u root -p mydb < /backup/mydb.sql
恢复压缩备份
gunzip < /backup/mydb.sql.gz | mysql -u root -p mydb
常见坑
坑1:密码写在命令行里不安全
mysqldump -u root -p123456 mydb > xxx.sql
这样密码会出现在进程列表里。
安全做法: 用配置文件存密码。
# /root/.my.cnf
[mysqldump]
user=root
password=你的密码
权限设600:
chmod 600 /root/.my.cnf
以后备份就不用输密码了:
mysqldump mydb > xxx.sql
坑2:备份时锁表
大库备份的时候,业务卡住了。
原因: 没加 --single-transaction。
解决: InnoDB库一定要加这个参数。
坑3:备份文件是空的
备份完一看,文件0字节。
原因: 密码错了,或者数据库名错了。
解决: 先手动执行一次,看报错。
坑4:备份没包含存储过程
备份后恢复,发现存储过程没了。
原因: mysqldump默认不备份存储过程。
解决: 加 --routines 参数。
总结
三步搞定自动备份:
- 写备份脚本
- 配crontab定时执行
- 定期清理旧备份
记住: 备份了不算数,能恢复才算数。
遇到问题加QQ23979811 协助处理
Linux crontab定时任务不执行常见原因与排查
前言
配置好了 crontab 定时任务,时间也写对了,但到点就是不执行?这是很多新手都会遇到的坑。crontab 不执行的原因五花八门,从最基础的语法错误到环境变量问题,再到 SELinux 拦截,排查起来很费时间。本文把常见的 crontab 不执行原因都整理出来,按排查顺序一步步来,基本能解决 90% 的问题。
第一步:确认 crond 服务是否运行
最基础也是最容易忽略的——定时任务服务本身有没有启动。
systemctl status crond
如果显示 inactive (dead),说明服务没启动,先启动它:
systemctl start crond
systemctl enable crond
CentOS7 上定时服务叫 crond,Debian/Ubuntu 上叫 cron,别搞混了。
第二步:检查 crontab 语法是否正确
1. 查看当前用户的定时任务
crontab -l
2. crontab 时间格式
* * * * * 命令
- - - - -
| | | | |
| | | | ----- 星期几 (0 - 7) (0和7都是周日)
| | | ------- 月份 (1 - 12)
| | --------- 日期 (1 - 31)
| ----------- 小时 (0 - 23)
------------- 分钟 (0 - 59)
3. 常见语法错误
错误1:分钟写在最前面
# 错误:每天9点执行(错的)
0 9 * * * /root/backup.sh
# 正确:每天9点执行
0 9 * * * /root/backup.sh
等等,这个其实是对的。真正容易错的是:
错误2:命令路径用了相对路径
# 错误:crontab 里找不到 mysql 命令
0 9 * * * mysql -u root -p123456 dbname < /root/backup.sql
# 正确:用绝对路径
0 9 * * * /usr/bin/mysql -u root -p123456 dbname < /root/backup.sql
crontab 的环境变量和你手动登录时不一样! 它的 PATH 很精简,很多命令找不到。所以要么用绝对路径,要么在脚本里先 source 环境变量。
第三步:检查脚本本身有没有执行权限
很多人写了脚本,但忘了加执行权限:
ls -l /root/backup.sh
如果没有 x 权限,加上:
chmod +x /root/backup.sh
第四步:检查脚本内容有没有问题
1. 脚本里用了相对路径
# 错误:相对路径在 crontab 里会出问题
cd /var/www/html
tar -zcf backup.tar.gz .
# 正确:用绝对路径
cd /var/www/html
tar -zcf /root/backup.tar.gz .
2. 脚本里用了交互式命令
比如 read、ssh 需要交互输入的命令,在 crontab 里是跑不了的,因为没有终端。
3. 脚本开头没写 shebang
# 正确的 shebang
#!/bin/bash
如果脚本没有 shebang,或者 shebang 写错了,执行就会失败。
第五步:检查日志
crontab 的执行日志默认在 /var/log/cron 里:
tail -100 /var/log/cron
如果日志里显示任务被执行了,但结果不对,那就是脚本本身的问题。
如果日志里压根没有这个任务的记录,那就是 crontab 服务本身的问题或者任务格式不对。
第六步:检查环境变量问题
这是最隐蔽的坑。你手动跑脚本没问题,但 crontab 里就是不行,十有八九是环境变量的问题。
排查方法:在脚本开头加上环境变量
#!/bin/bash
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
或者直接在 crontab 里指定:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
0 9 * * * /root/backup.sh
第七步:检查 SELinux 是不是拦截了
如果 SELinux 开着,可能会拦截 crontab 的操作。查看日志:
tail -100 /var/log/audit/audit.log | grep crontab
如果看到 denied 字样,就是 SELinux 拦截了。
第八步:检查邮箱通知
crontab 默认会把执行结果(包括错误信息)发到执行用户的邮箱里:
mail
或者看 /var/spool/mail/root 文件:
tail -100 /var/spool/mail/root
这里面通常会有具体的错误信息,是排查的好帮手。
常见坑
坑1:环境变量缺失
这是最常见的坑。你手动跑脚本能成,crontab 里就不行,因为 crontab 的环境变量和你登录 shell 的不一样。
解决: 在脚本开头加上 export PATH=...,或者所有命令都用绝对路径。
坑2:脚本没有执行权限
写完脚本忘了 chmod +x,crontab 当然跑不了。
坑3:重定向丢了
crontab 默认会把输出发到邮箱,如果你不想收邮件,可以重定向:
0 9 * * * /root/backup.sh > /dev/null 2>&1
> /dev/null 是丢弃标准输出,2>&1 是把错误输出也重定向到标准输出。
坑4:时间格式写错了
比如把 * * * * * 写成了 * * * *,少了一个字段,直接就不执行。
坑5:用户写错了
你用 root 用户写的 crontab,但实际是别的用户需要跑,当然不执行。用 crontab -u 用户名 -l 查看对应用户的任务。
总结
crontab 不执行的排查顺序:
- 先确认 crond 服务启动了没
- 检查 crontab 语法格式对不对
- 脚本有没有执行权限
- 脚本里的命令是不是都用了绝对路径
- 看
/var/log/cron日志有没有执行记录 - 看
/var/spool/mail/root有没有错误信息 - 检查 SELinux 是不是拦截了
按这个顺序排查,基本都能找到原因。记住 crontab 的环境变量和你手动登录时不一样,所有命令都用绝对路径,能避免 80% 的问题。
遇到问题加QQ23979811 协助处理