最稳做法是用Linux Cron定期执行php think your:command命令,命令内用Db::execute()跑SQL;禁用Swoole tick或think-worker等常驻进程方案,因其在PHP-FPM下不可靠、易重复执行或阻塞服务。

ThinkPHP 本身不内置定时器,跑 SQL 或做后台任务,必须靠外部调度触发命令行任务,而不是在框架里“启动一个常驻进程”来轮询。这是最稳、最易排查、最符合生产环境规范的做法。
Linux Cron 怎么调用 ThinkPHP 命令执行 SQL
核心是让 cron 定期执行 php think your:command,命令内部用 Db::execute() 或 Db::query() 跑原生 SQL。
- 先写一个命令类:
php think make:command DbCleanTask,在execute()里写Db::execute('DELETE FROM logs WHERE create_time - 确保该命令在本地能直接运行:
cd /var/www/myapp && php think db:clean-task,无报错再进 cron - Crontab 示例(每小时清理一次):
0 * * * * cd /var/www/myapp && php think db:clean-task >> /var/www/myapp/storage/logs/db-cron.log 2>&1 - 注意路径必须用绝对路径,
cd不要省;2>&1必须带上,否则错误全丢进黑洞
为什么别用 Swoole tick() 或 think-worker 在 Web 进程里跑定时 SQL
这类方案看似“自动”,实则隐患密集:Web 请求进程生命周期不可控,tick() 可能被重启中断、多 Worker 重复执行、SQL 执行卡住导致整个 HTTP 服务假死。
-
think-worker启动的是独立 Worker 进程,但需额外维护进程状态、日志、重启逻辑,和 Cron 相比运维成本翻倍 - Swoole 的
Coroutine::sleep()+tick()在 CLI 模式下可用,但一旦部署在 Nginx + PHP-FPM 环境,根本不会生效——FPM 不支持协程长期驻留 - SQL 执行耗时波动大,固定间隔(如 5 秒)容易堆积,而 Cron 是离散触发,天然防重入
如何监控这些后台 SQL 任务是否真在跑
不能只看 cron 日志有没有输出,得验证 SQL 是否真执行、有没有报错、有没有锁表或慢查询。
立即学习“PHP免费学习笔记(深入)”;
- 在命令的
execute()开头加Log::info('db:clean-task start at ' . date('Y-m-d H:i:s')),结尾加Log::info('db:clean-task done, affected ' . $result) - 把 SQL 改成带
SELECT ROW_COUNT()或捕获Db::execute()返回值,记录影响行数,避免“看似成功实则没删任何数据” - 数据库层加监控:MySQL 开启
slow_query_log,定期查information_schema.PROCESSLIST看是否有长事务阻塞你的定时 DELETE/UPDATE - 别依赖
crontab -l显示就认为任务在跑——它只说明规则存在,不保证执行权限、路径、PHP 版本、扩展都 OK
真正难的不是写那几行 SQL,而是让每次执行都可追溯、可对账、可快速止血。Cron + ThinkPHP 命令 + 显式日志 + 数据库反馈,这四件套缺一不可。



















