通知必须异步发送,实现ShouldQueue接口并配置QUEUE_CONNECTION为redis/sqs等非sync驱动,避免数据库队列死锁;构造函数需预加载必要数据,不可在队列中触发懒加载或依赖HTTP上下文。

通知必须异步发送,同步执行会卡住请求响应,尤其发邮件或调第三方 API 时。只要没走队列,就不是真正意义上的“优化”。
通知类怎么加队列支持
关键不是“加队列”,而是让 Laravel 知道这个通知该进队列。最直接的方式是让通知类实现 ShouldQueue 接口:
- 在通知类顶部加上
use Illuminate\Contracts\Queue\ShouldQueue; - 类声明改为
class OrderShipped extends Notification implements ShouldQueue - 不用手动写
dispatch()或改构造函数——Laravel 在调用$user->notify()时自动识别接口并推入队列
别漏掉 use Queueable; trait(Laravel 10+ 默认生成时已含),它提供序列化/反序列化支持,否则队列消费时会报 Undefined property: $queue 错误。
QUEUE_CONNECTION=database 能不能用
能,但只适合开发或低频场景。数据库队列本质是轮询 jobs 表,高并发下容易触发 MySQL 行锁和连接耗尽,日志里常出现 SQLSTATE[HY000]: General error: 1205 Deadlock found。
- 生产环境务必用
redis或sqs;database驱动的retry_after值必须小于 MySQL 的wait_timeout(默认 28800 秒),否则任务超时后连接断开,任务卡死不重试 - 如果非要用 database,确保
config/queue.php中'table' => 'jobs'对应的迁移已执行:php artisan queue:table && php artisan migrate -
QUEUE_CONNECTION=database写在.env里后,必须重启queue:work进程才生效,不会热加载
为什么 notify() 没进队列,还是同步执行
常见原因不是代码写错,而是环境或配置断层:
- 检查
.env:确认QUEUE_CONNECTION不是sync(这是假队列,立刻执行) - 运行
php artisan queue:work --once手动触发一次,看日志是否出现Processing job;没出现说明根本没进队列 - 通知类没实现
ShouldQueue接口,或接口名拼错(比如写成ShouldQueued) - 模型没引入
Notifiabletrait,$user->notify()调用的是 Eloquent 原生方法,不走通知系统 - 队列 worker 没在运行:
php artisan queue:work必须持续运行,不是执行一次就完事
通知内容里传模型要小心什么
通知类构造函数接收 $order 这种 Eloquent 模型时,Laravel 会自动序列化。但序列化过程不包含未加载的关系数据,也不触发懒加载:
- 如果
toMail()里用了$this->order->customer->name,而$order创建时没->with('customer'),结果就是null - 更危险的是:在
toDatabase()或toMail()里主动调用$this->order->customer,会触发同步查询,彻底破坏异步意义 - 正确做法是在构造函数里就把需要的数据取出来,转成数组或值对象:
$this->customerName = $order->customer->name;
队列任务没有 HTTP 上下文,auth、session、request 全部不可用,所有依赖数据必须在分发前准备好。


















