定时任务卡住需先查cron日志(/var/log/syslog或/cron)和php残留进程;确保php路径正确、脚本有执行权限;禁用web环境pcntl_fork;高并发用redis setnx或数据库唯一索引防重;亚分钟级用systemd timer而非cron。

定时任务卡住不执行?先查 cron 日志和 php 进程状态
PHP 本身没有内置定时调度器,所谓“定时任务”实际依赖系统级 cron 或第三方进程管理器。任务不跑,90% 不是 PHP 写错了,而是 cron 没触发、php 命令路径不对、或脚本被上一个没退出的实例锁死。
-
cron日志默认在/var/log/syslog(Ubuntu)或/var/log/cron(CentOS),搜CRON和你的脚本名 - 用
ps aux | grep php看有没有残留进程卡在sleep或数据库wait上 - 确保
cron里写的php路径和 CLI 实际一致:运行which php,别直接写php script.php - 脚本开头加
#!/usr/bin/env php并给执行权限(chmod +x),否则cron可能静默失败
pcntl_fork 在 Web 环境下根本不能用
有人想用 pcntl_fork 让 PHP 自己“多线程”跑定时逻辑,这在 Apache/Nginx + PHP-FPM 下必然失败——FPM worker 是阻塞模型,pcntl 扩展默认被禁用,且 fork 出的子进程无法被父进程可靠回收,极易泄漏内存和文件描述符。
-
pcntl_fork只能在 CLI 模式下安全使用,且必须配对pcntl_wait或pcntl_waitpid - Web 请求中调用它,大概率触发
Warning: pcntl_fork(): Cannot fork in non-CGI mode - 真要并发执行多个定时动作,用消息队列(如 Redis
LPUSH+ 单独消费者进程)比硬 fork 更稳
高并发场景下,重复执行怎么防?别只靠文件锁
当每分钟有几十个任务要跑,又都用 flock 锁同一个文件,会排队堵死;而用数据库唯一索引或 Redis SETNX,才是可伸缩的方案。
- 文件锁(
flock($fp, LOCK_EX | LOCK_NB))在 NFS 或容器挂载卷上可能失效 - 用 MySQL 插入带
UNIQUE KEY (task_name, date)的记录,失败即说明已在运行 - Redis 更轻量:
SET lock:task_name "1" NX EX 300,设 5 分钟过期,避免死锁 - 别忘了清理:任务成功后删锁,异常退出时靠过期自动释放
时间精度不够?cron 最小粒度就是 1 分钟
如果业务要求“每 15 秒跑一次”,cron 无解。强行用 * * * * * sleep 15; php task.php 会导致每分钟启动 4 个进程,失控风险极高。
立即学习“PHP免费学习笔记(深入)”;
- 真正需要 sub-minute 精度,得换调度器:Supervisor +
php task.php --loop配sleep(15),但要自己管好异常退出和重启 - 更推荐用
systemd timer(Linux ):支持OnUnitActiveSec=15s,且自带依赖管理和日志追踪 - 注意时区:
cron默认用系统时区,PHP 脚本里date_default_timezone_set()不影响它
最麻烦的从来不是写代码,而是让成百上千个定时任务在不同机器、不同负载下持续不漏、不重、不僵。锁机制选型、进程生命周期管理、日志可追溯性——这些细节不提前压住,半夜告警时再补就晚了。











