Laravel 9中如何在Dispatchable对象序列化入库后释放内存?
问题解决:Laravel批量发送大体积邮件内存耗尽问题
问题背景
在Laravel 9环境下批量给大量用户发送大体积邮件时,遭遇服务器内存耗尽的问题:
- 自定义
ProcessEmail类实现了ShouldQueue和Dispatchable接口,类内私有字段存储大体积邮件正文 - 对象序列化后存入
jobs表的存储开销可接受,但循环遍历用户时,相关对象持续滞留在内存中,导致内存占用飙升 - 最初尝试在对象写入数据库后用
unset()释放内存,但未解决问题
排查过程
深入排查后确认,内存占用过高并非ProcessEmail对象本身导致,而是项目中监听数据变更并记录日志的代码引发的额外内存开销——这类代码会在Eloquent操作过程中留存大量上下文数据,无法被及时回收。
最终解决方案
放弃使用Eloquent,直接通过PDO原生操作将邮件任务数据写入jobs表,彻底规避Eloquent带来的额外内存消耗:
// 替代Eloquent的PDO写入方式 $pdo = DB::connection()->getPdo(); $stmt = $pdo->prepare("INSERT INTO jobs (queue, payload, attempts, reserved_at, available_at, created_at) VALUES (?, ?, ?, ?, ?, ?)"); foreach ($users as $user) { // 构建符合Laravel队列规范的payload $payload = json_encode([ 'displayName' => ProcessEmail::class, 'job' => 'Illuminate\Queue\CallQueuedHandler@call', 'maxTries' => null, 'timeout' => null, 'timeoutAt' => null, 'data' => [ 'commandName' => ProcessEmail::class, 'command' => serialize(new ProcessEmail($user, $largeEmailBody)) ] ]); $stmt->execute([ 'default', // 目标队列名称 $payload, 0, // 初始尝试次数 null, now()->getTimestamp(), // 任务可用时间 now()->getTimestamp() // 任务创建时间 ]); }
关键说明
- 直接使用PDO绕过了Eloquent的事件监听、模型实例管理等环节,避免了日志监听代码触发的内存累积
- 手动构建队列任务的
payload需严格匹配Laravel队列的格式,确保任务能被队列处理器正常消费
内容的提问来源于stack exchange,提问作者Magmatic
相关产品推荐
相关产品推荐

