Laravel队列Worker生产环境插入数据库时冻结,本地运行正常
Laravel队列Worker生产环境插入数据库冻结问题排查方案
问题背景
- 业务流程:每10分钟从MQTT拉取10条数据存入Redis队列,由队列Worker处理后插入数据库
- 异常现象:第一批10条数据插入成功,第二批数据到达时Worker进程已启动,但在插入数据库阶段冻结
- 环境差异:本地连接生产数据库时一切正常,仅生产环境出现该问题
- 已完成排查:Laravel日志无相关错误、数据库日志无死锁/慢查询、无重复任务调度/重叠事务、服务器CPU/内存及数据库连接均无异常
排查与解决方案
1. 修复Worker数据库连接复用问题
生产环境Worker为常驻进程,可能存在长时间持有的数据库连接失效但未自动重建的情况:
- 在任务类的
handle方法开头手动重新建立连接:
DB::reconnect();
- 启动Worker时设置合理的超时参数,避免进程长时间运行导致连接失效:
php artisan queue:work redis --timeout=30 --tries=3
- 检查
config/database.php中数据库连接的timeout和keepalive配置,确保适配生产环境数据库的连接超时规则。
2. 验证Redis队列序列化与网络问题
虽然本地正常,但生产环境Redis可能存在数据序列化或网络传输差异:
- 修改队列配置为
json序列化(默认php序列化可能在生产环境出现兼容性问题),编辑config/queue.php:
'redis' => [ 'driver' => 'redis', 'connection' => 'default', 'queue' => env('REDIS_QUEUE', 'default'), 'retry_after' => 90, 'block_for' => null, 'serializer' => 'json', ],
- 检查生产环境Redis的内存使用、键过期策略,确保队列任务数据未损坏。
3. 添加任务级详细日志定位卡点
全局日志无报错时,在任务handle方法中添加步骤日志,精准定位冻结环节:
public function handle() { Log::info('启动第二批任务处理,数据量:'.count($this->data)); foreach ($this->data as $index => $item) { Log::info('开始处理第'.$index.'条数据:'.json_encode($item)); try { DB::table('target_table')->insert($item); Log::info('第'.$index.'条数据插入成功'); } catch (\Exception $e) { Log::error('第'.$index.'条数据插入失败:'.$e->getMessage(), ['data' => $item]); throw $e; } } Log::info('第二批任务处理完成'); }
通过日志排查是否存在特定数据(如特殊字符、超长字段)导致的插入阻塞。
4. 切换Worker运行模式测试
若使用queue:work常驻进程模式,临时改为queue:listen(每次任务重启进程)测试:
php artisan queue:listen redis --timeout=30 --tries=3
如果queue:listen运行正常,说明常驻进程的资源持有导致问题,可通过Supervisor配置Worker自动重启策略(如每小时重启一次)。
5. 排查数据库连接池与超时配置
- 检查生产环境数据库的
wait_timeout和interactive_timeout参数,若连接闲置超时被数据库主动断开,Worker未检测到会导致后续操作冻结。 - 若使用数据库连接池,验证最大连接数、等待超时时间配置,确保Worker请求连接时不会因池耗尽阻塞。
6. 单独测试任务执行
在生产环境手动触发任务执行,排除队列调度干扰:
- 直接调用任务类的
handle方法,模拟数据插入 - 使用
php artisan queue:work redis --once单独处理一条队列任务,观察执行状态
内容的提问来源于stack exchange,提问作者Otmane Maacha
相关产品推荐
相关产品推荐

