Laravel定时任务需系统Cron触发、withoutOverlapping()或Redis锁防重、关键任务加幂等校验。仅注册不执行,多实例必须用Redis锁,避免重复运行。

要让 Laravel 定时任务真正跑起来,光写 $schedule->command('backup')->daily() 是不够的。它只是注册逻辑,不自动执行;必须靠系统 Cron 或 Supervisor 驱动调度器轮询,同时得处理多实例下的重复执行问题。核心就三件事:触发机制、锁控制、防重保障。
必须配系统 Cron,否则任务永远不执行
Laravel 调度器不是后台服务,它只在 schedule:run 被调用时检查一次该不该执行任务。没 Cron,就等于没人按表看时间,所有任务都躺在内存里不动。
- 在服务器上运行
crontab -e,添加这一行(路径替换成你项目的绝对路径):* * * * * cd /var/www/myapp && php artisan schedule:run >> /dev/null 2>&1 - 确认 cron 服务已启动:
sudo systemctl status cron(Ubuntu/Debian)或sudo systemctl status crond(CentOS/RHEL) - 开发时可用
php artisan schedule:run手动触发,验证命令是否注册成功、输出是否正常
单机防重靠 withoutOverlapping(),但默认只锁本地文件
withoutOverlapping() 是 Laravel 内置的防重叠机制,原理简单:执行前查锁文件是否存在,存在就跳过;执行完自动删锁。锁文件默认放在 storage/framework/cache/data/ 下,形如 schedule-backup.lock。
- 这个机制在 Homestead、Docker 或 NFS 挂载环境下容易失效——权限不足、缓存延迟、SELinux 限制都会导致锁文件创建失败或读取不准
- 临时排查可加
->withoutOverlapping(0):0 表示不等待直接跳过,能快速判断是不是锁机制干扰了执行 - 它只对单台服务器有效,无法跨进程或跨机器同步状态
多实例部署必须用 Redis 锁,不能依赖文件
当应用跑在 Kubernetes Pod、负载均衡后多个 PHP 实例,或用 Supervisor 启了多个 schedule:work 进程时,每个节点都会独立运行 schedule:run,结果就是每天发 5 遍邮件、清 5 次缓存。
- 推荐用
cache()->store('redis')->lock()手动实现分布式锁,比自己拼Cache::add()更安全,支持自动续期和异常释放 - 锁键建议带环境标识,例如
schedule:send-daily-report:production,避免测试服误触生产锁 - 若不想改代码,Laravel 原生提供
onOneServer()方法,但它依赖数据库锁(需配置DB_CONNECTION),且仅保证「某一时刻只有一个节点执行」,不解决首次竞争问题
高频任务建议加缓存状态校验,双重保险
即使用了 Redis 锁,极端情况下(如节点崩溃未释放锁、TTL 设置过短),仍可能漏防。对关键任务(如支付对账、库存同步),可在任务主体开头再做一层幂等判断。
- 生成业务唯一键,比如
"reconcile_orders_20261022",用Cache::add($key, true, 7200)尝试写入(2 小时 TTL) - 返回
false直接退出,不执行后续逻辑;返回true才继续 - 务必用
Cache::add(),不要用Cache::has() + Cache::put(),后者非原子操作,高并发下会失效


















