Laravel 事务提交后再派发队列
2026-08-18 10:00老强
Laravel 事务提交后再派发队列
Laravel 里写队列很顺手。订单创建成功,发一个短信任务;表单提交成功,发一个同步任务;支付回调处理完,发一个通知任务。
代码看起来没问题,但在队列跑得很快、数据库压力比较大,或者读写库有延迟时,它会出一些很难复现的问题。
一、问题在哪里
事务还没提交,队列任务可能已经开始跑了。
DB::transaction(function () use ($request) {
// 创建订单主记录
$order = Order::create([
'order_no' => $request->input('order_no'),
'status' => 'pending',
]);
// 派发订单后续处理任务
ProcessOrderJob::dispatch($order->id);
});
1. Laravel 开启事务
2. 写入订单
3. dispatch 队列任务
4. worker 拿到任务并开始执行
5. worker 查询订单
6. 事务还没提交,订单查不到
7. 事务提交
二、不要在事务中间急着 dispatch
最简单的做法,是把任务派发放到事务后面。
$order = null;
DB::transaction(function () use ($request, &$order) {
// 事务里只做数据库写入
$order = Order::create([
'order_no' => $request->input('order_no'),
'status' => 'pending',
]);
});
// 事务提交后,再派发队列任务
ProcessOrderJob::dispatch($order->id);
三、afterCommit 更直接
如果项目版本支持,可以让队列任务等事务提交后再派发。
DB::transaction(function () use ($request) {
// 创建订单主记录
$order = Order::create([
'order_no' => $request->input('order_no'),
'status' => 'pending',
]);
// 等当前事务提交后,再真正派发任务
ProcessOrderJob::dispatch($order->id)->afterCommit();
});
四、任务里不要传整块模型
队列任务里尽量传 ID,不要传一整块模型。
不推荐:
ProcessOrderJob::dispatch($order);
更稳一点:
ProcessOrderJob::dispatch($order->id);
public function handle()
{
// 任务执行时重新读取最新订单数据
$order = Order::find($this->orderId);
if (!$order) {
return;
}
// 继续处理订单
}
五、任务最好是幂等的
事务提交后再派发,只能解决“读不到未提交数据”的问题。队列任务本身还可能重复执行。
public function handle()
{
// 重新读取订单,避免使用过期状态
$order = Order::find($this->orderId);
if (!$order) {
return;
}
// 已处理过的订单直接跳过
if ($order->status === 'processed') {
return;
}
// 执行业务处理
$this->process($order);
// 标记处理完成
$order->update([
'status' => 'processed',
]);
}
六、多个任务怎么处理
事务里有时候会做很多事。这时候可以先收集任务参数,事务外统一派发。
$jobs = [];
DB::transaction(function () use ($request, &$jobs) {
// 创建订单主记录
$order = Order::create([
'order_no' => $request->input('order_no'),
'status' => 'pending',
]);
// 只收集任务需要的参数,不在事务中间派发
$jobs[] = new ProcessOrderJob($order->id);
$jobs[] = new SendOrderNoticeJob($order->id);
});
foreach ($jobs as $job) {
dispatch($job);
}
七、什么时候可以在事务里派发
不是所有任务都必须等事务提交。关键看任务会不会读取事务里刚写入的数据。
任务会读事务里写的数据:提交后派发
任务不会读事务里写的数据:可以按实际情况处理
拿不准:提交后派发
八、最后
Laravel 的事务和队列都很好用。坑通常不在工具本身,而在两个工具接在一起时,执行顺序没有想清楚。
事务里只做数据库写入
队列任务等事务提交后再派发
任务参数尽量传 ID
任务执行时重新查数据
任务逻辑尽量幂等
这几条看起来朴素,但线上很管用。
曝光355浏览28

