本文目录导读:

在 Laravel 中处理事务嵌套是一个常见需求,Laravel 提供了优雅的解决方案,以下是详细的处理方法和最佳实践:
Laravel 事务嵌套的基本机制
Laravel 的事务系统通过保存点(Savepoints) 来支持嵌套事务,当你在事务中再开启事务时,Laravel 会创建保存点而不是真正开启新事务。
// 外层事务
DB::beginTransaction();
try {
// 业务逻辑1
// 内层事务(实际上创建保存点)
DB::beginTransaction();
try {
// 业务逻辑2
DB::commit(); // 回滚到保存点,不真正提交
} catch (\Exception $e) {
DB::rollBack(); // 回滚到保存点
throw $e;
}
DB::commit(); // 真正提交
} catch (\Exception $e) {
DB::rollBack(); // 回滚整个事务
throw $e;
}
使用闭包方式(推荐)
DB::transaction(function () {
// 外层事务操作
DB::table('users')->update(['status' => 1]);
// 嵌套事务
DB::transaction(function () {
DB::table('orders')->insert(...);
// 如果这里抛出异常,只会回滚到内层事务开始前的状态
});
// 外层继续操作
});
检查当前事务层级
// 查看当前事务嵌套层级
$level = DB::transactionLevel();
echo "当前事务层级: {$level}"; // 0 表示没有事务
// 检查是否在事务中
$inTransaction = DB::transactionLevel() > 0;
高级嵌套事务处理
1 创建自定义事务管理器
<?php
namespace App\Services;
use Illuminate\Support\Facades\DB;
class TransactionManager
{
/**
* 执行带嵌套支持的事务
*/
public function execute(callable $callback, int $attempts = 1)
{
// 记录当前事务层级
$currentLevel = DB::transactionLevel();
try {
if ($currentLevel === 0) {
// 最外层事务
return DB::transaction($callback, $attempts);
}
// 嵌套事务:使用保存点
DB::beginTransaction();
try {
$result = $callback();
DB::commit();
return $result;
} catch (\Throwable $e) {
DB::rollBack();
throw $e;
}
} catch (\Throwable $e) {
if ($currentLevel > 0) {
// 标记需要回滚
throw $e;
}
// 最外层事务的错误处理
if ($attempts > 1) {
// 重试逻辑
return $this->retry($callback, $attempts);
}
throw $e;
}
}
protected function retry(callable $callback, int $attempts)
{
$currentAttempt = 1;
while ($currentAttempt <= $attempts) {
try {
return $callback();
} catch (\Throwable $e) {
if ($currentAttempt === $attempts) {
throw $e;
}
$currentAttempt++;
usleep(100 * 1000); // 100ms 延迟
}
}
}
}
2 使用服务容器绑定
// 在 AppServiceProvider 中注册
app()->singleton('transaction.manager', function () {
return new \App\Services\TransactionManager();
});
// 使用
$manager = app('transaction.manager');
$result = $manager->execute(function () {
// 业务逻辑
});
处理异常和回滚状态
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class OrderService
{
public function createOrder(array $data)
{
$currentLevel = DB::transactionLevel();
try {
return DB::transaction(function () use ($data) {
// 创建订单
$order = Order::create($data);
// 更新库存(嵌套事务)
$this->updateInventory($order);
// 删除缓存(假设失败)
$this->clearOrderCache($order->id);
return $order;
}, 3); // 最多重试3次
} catch (\Exception $e) {
Log::error('创建订单失败', [
'level' => $currentLevel,
'error' => $e->getMessage()
]);
throw $e;
}
}
protected function updateInventory(Order $order)
{
DB::transaction(function () use ($order) {
$product = Product::find($order->product_id);
$product->decrement('stock', $order->quantity);
// 如果库存不足,抛出异常
if ($product->stock < 0) {
throw new \Exception('库存不足');
}
});
}
}
最佳实践建议
1 使用统一的封装函数
// app/helpers.php
if (!function_exists('transactional')) {
function transactional(callable $callback, int $attempts = 1)
{
return app('transaction.manager')->execute($callback, $attempts);
}
}
// 使用示例
$result = transactional(function () {
// 业务逻辑
});
2 避免过深嵌套
public function complexOperation()
{
return DB::transaction(function () {
// 1. 主业务流程
// 2. 尽量通过函数抽象,避免深层嵌套
$this->processPayment();
$this->updateOrderStatus();
$this->notifyCustomer();
// 如果某个步骤失败,整个事务回滚
});
}
protected function processPayment()
{
// 内部使用自己的事务逻辑
if (DB::transactionLevel() > 0) {
// 已经在事务中,直接执行
$this->paymentService->process();
} else {
DB::transaction(function () {
$this->paymentService->process();
});
}
}
3 监控和日志
DB::listen(function ($query) {
if ($query->time > 1000) {
Log::warning('慢查询', [
'sql' => $query->sql,
'bindings' => $query->bindings,
'time' => $query->time,
'transaction_level' => DB::transactionLevel()
]);
}
});
常见问题和解决方案
1 死锁处理
try {
DB::transaction(function () {
// 业务逻辑
}, 5); // 设置重试次数
} catch (\Illuminate\Database\QueryException $e) {
if ($e->getCode() == 40001) { // MySQL 死锁错误码
Log::error('死锁发生,已重试');
}
throw $e;
}
2 避免在循环中开启事务
// 错误示例
foreach ($items as $item) {
DB::transaction(function () use ($item) {
// 每个循环都开启事务
});
}
// 正确示例
DB::transaction(function () use ($items) {
foreach ($items as $item) {
// 所有操作在一个事务中
}
});
Laravel 的事务嵌套功能强大且灵活,正确使用保存点和层级管理,可以构建可靠的数据操作流程,关键是根据实际业务需求选择合适的嵌套方式和错误处理策略。