crontab中php脚本执行失败主因是环境差异:path受限导致php命令找不到,应使用绝对路径;数据库/redis连接失败因localhost强制走socket,需改用127.0.0.1;脚本卡住常因内存限制、超时或后台进程被回收;多任务并发需用flock或数据库锁防重入。

crontab 里直接调用 php 命令行失败?路径和环境变量是主因
很多同学写好 php /path/to/script.php 放进 crontab,结果脚本不执行、没日志、也没报错——大概率是 crontab 的执行环境和你终端完全不同。它不读 ~/.bashrc,PATH 只有 /usr/bin:/bin,连 php 命令都可能找不到。
- 用绝对路径调用
php:先运行which php,比如输出/usr/local/bin/php,那就必须写成/usr/local/bin/php /path/to/script.php - 脚本开头加
#!/usr/local/bin/php并chmod +x后,crontab 里可直接写脚本路径,但依然要确保该脚本里所有外部命令(如curl、mysql)也用绝对路径 - 临时调试时,在 crontab 行末尾加
> /tmp/cron-debug.log 2>&1,看真实错误输出
PHP 脚本在 cron 下连接数据库或 Redis 失败?别信 localhost
你在终端能连上 localhost 的 MySQL,cron 里却报 Connection refused 或超时,不是权限问题,而是 localhost 在 CLI 和 socket 连接语义下行为不同:PHP 的 mysqli 或 PDO 遇到 localhost 会强制走 Unix socket;而 cron 环境下 socket 文件路径可能不在默认位置,或者权限不对。
- 把数据库主机从
localhost换成127.0.0.1,强制走 TCP,绕过 socket 权限问题 - 检查 Redis 连接是否用了
unix:///var/run/redis/redis.sock这类路径——cron 用户可能无权访问该 socket 文件,同样改用127.0.0.1:6379 - 如果必须用 socket,确认
www-data或 cron 所属用户对 socket 文件有读写权限,并在 PHP 中显式指定完整路径
脚本执行一半卡住或被 kill?注意内存、超时和后台进程
web 请求里能跑通的 PHP 脚本,丢进 cron 后突然没反应、日志停在中间,常见于三类情况:CLI 模式内存限制更低、脚本内部 sleep 或 shell exec 阻塞、或者 fork 出子进程后父进程退出导致孤儿进程被系统回收。
- 在脚本开头加
ini_set('memory_limit', '512M');,CLI 默认常是 128M,远低于 web 配置 - 避免在 cron 脚本里用
exec('sleep 30 &')这类后台启动——cron 进程退出后,子进程容易被 init 接管并受资源策略限制 - 用
set_time_limit(0)关闭超时,但更要检查是否有死循环或未设 timeout 的file_get_contents()、cURL 请求 - 如果脚本本身需要长时间运行,考虑用
nohup /usr/local/bin/php /path/to/script.php > /dev/null 2>&1 &包一层,但得自行处理重复触发问题
多个定时任务互相干扰?用锁文件或数据库标记防重入
每分钟跑一次的统计脚本,如果某次执行慢(比如网络抖动),下次 cron 触发时旧进程还没结束,两个实例同时写同一张表,数据就乱了。crontab 本身不提供互斥机制。
- 最轻量做法:脚本开头生成锁文件
/tmp/myscript.lock,用flock -n尝试获取独占锁,失败则 exit - 更稳一点:在数据库建一张
task_lock表,用INSERT ... ON DUPLICATE KEY UPDATE插入带时间戳的唯一任务名,成功才继续执行 - 别用
ps aux | grep script.php判断——竞态条件明显,两进程几乎同时查,都以为没人跑,然后一起进 - 锁文件记得在脚本退出前
unlink(),但更要加上register_shutdown_function()保底清理,防止异常中断留死锁
真正麻烦的从来不是“怎么让脚本跑起来”,而是“怎么让它每次只跑一个、跑完不留尾巴、出错还能定位”。环境差异、资源边界、并发控制——这三块漏掉任何一块,cron 就从自动化工具变成定时甩锅机。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











