Laravel批量插入25万条数据遇PHP超时问题(多租户+队列)
问题
使用Laravel Eloquent向MySQL插入25万条记录时遭遇PHP执行超时,已尝试Laravel Queue但问题依旧。当前环境约束:
- 前端:Angular
- 多租户:基于Tenancy for Laravel实现
已用代码
任务文件代码
class MedicineData implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, Batchable; public $data; public function __construct($data) { $this->data = $data; } public function handle(): void { foreach ($this->data as $key => $value) { $medicine = new MedicineName(); $medicine->name = $value['name']; $medicine->save(); } } }
控制器文件代码
$chunks = array_chunk($data, 500); $batch = Bus::batch([])->dispatch(); foreach ($chunks as $key => $value) { $batch->add(new MedicineData($value)); }
解决方案
- 替换循环单条保存为Eloquent批量插入
队列任务里循环调用save()会生成500条独立SQL,执行效率极低,这才是超时的核心原因。改成批量插入,单条SQL完成500条数据插入:
public function handle(): void { $insertData = collect($this->data)->map(fn($item) => ['name' => $item['name']])->toArray(); MedicineName::insert($insertData); }
- 调整队列Worker的超时配置
队列Worker自身有执行时间限制,默认可能过短。在config/queue.php对应驱动下修改timeout参数,或者启动Worker时用命令行参数覆盖:
// config/queue.php 中database驱动配置示例 'database' => [ 'driver' => 'database', 'table' => 'jobs', 'queue' => 'default', 'retry_after' => 90, 'timeout' => 60, // 按需调整,单位秒 ],
启动命令:
php artisan queue:work --timeout=60
缩小Chunk尺寸
如果500条批量插入仍超时,把array_chunk的第二个参数降到200或100,减少单任务处理量,降低单任务执行时间。确保队列任务中多租户上下文正确
多租户环境下,队列执行时必须切换到对应租户的上下文,否则会导致数据库连接异常或性能损耗。可以在任务构造时传入租户ID,在handle方法中初始化租户:
// 修改任务类 public $data; public $tenantId; public function __construct($data, $tenantId) { $this->data = $data; $this->tenantId = $tenantId; } public function handle(): void { tenancy()->initialize(Tenant::find($this->tenantId)); $insertData = collect($this->data)->map(fn($item) => ['name' => $item['name']])->toArray(); MedicineName::insert($insertData); }
控制器中传参:
$tenantId = tenant()->id; // 获取当前租户ID $chunks = array_chunk($data, 500); $batch = Bus::batch([])->dispatch(); foreach ($chunks as $chunk) { $batch->add(new MedicineData($chunk, $tenantId)); }
- 临时关闭模型事件
如果MedicineName模型有created、saving等事件监听,批量插入时会触发数百次事件,增加执行时间。可以临时关闭:
public function handle(): void { MedicineName::withoutEvents(function () { $insertData = collect($this->data)->map(fn($item) => ['name' => $item['name']])->toArray(); MedicineName::insert($insertData); }); }
内容的提问来源于stack exchange,提问作者Rizwan Ansari
相关产品推荐
相关产品推荐

