Laravel事务不回滚是因为未让异常透出回调函数;需避免try/catch吞异常、return提前退出,禁用嵌套事务,模型事件与Observer操作须手动纳入事务,严禁在事务中执行HTTP等IO操作。

事务没回滚,是因为没捕获异常
Laravel 默认不会自动回滚事务,哪怕数据库操作中途出错。关键点在于:只有在 DB::transaction() 回调里抛出未被捕获的异常,Laravel 才会触发回滚。如果用了 try/catch 却没重新 throw,或者用 return false 这类软退出,事务就照常提交。
常见错误现象:DB::transaction() 里执行了多条 SQL,第二条失败但第一条仍写入数据库;日志显示 “QueryException”,但数据已部分落库。
- 必须让异常穿透回调函数,不要在回调内吞掉异常
- 避免在事务块里用
if (xxx) return;提前退出——这不触发回滚 - 若需条件控制,用
throw new RuntimeException('业务不满足')显式中断
嵌套事务在 Laravel 里根本不存在
MySQL 不支持真正的嵌套事务,Laravel 的 DB::transaction() 也一样。所谓“嵌套”,其实是对同一连接的重复 begin,实际只生效最外层。内层 commit 或 rollback 不起作用,甚至可能报错 There is no active transaction。
使用场景:比如服务类 A 调用服务类 B,两者都写了 DB::transaction() ——结果是 B 的事务块被忽略,A 的事务控制全局。
- 不要指望内层事务能独立提交或回滚
- 想隔离逻辑,改用 savepoint(
DB::transactionLevel()+DB::rollBackTo()) - 更稳妥的做法是把事务边界上提到最外层调用处,内部方法只负责执行 SQL
模型事件和 Observer 可能破坏事务一致性
Eloquent 模型的 creating、saving 等事件,以及 Observer 中的逻辑,如果在事务中触发并执行了额外数据库操作(比如记录日志表、更新缓存),这些操作默认不在当前事务保护范围内——除非你手动把它们也塞进同一个 DB::transaction() 块里。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
容易踩的坑:用户创建成功但通知日志没写入;订单创建后库存扣减了,但审计日志因异常丢失,导致对账困难。
- Observer 里的 DB 操作不会自动加入外层事务
- 若需强一致性,把相关逻辑统一收口到事务块内,别依赖事件自动传播
- 慎用
static::created(function () { ... })做跨表变更,它脱离当前事务上下文
长时间事务会卡死连接池,别在事务里做 HTTP 请求
事务开启后,数据库连接一直被占用,直到 commit 或 rollback。如果在 DB::transaction() 里调用外部 API(比如支付回调验证、发短信),网络延迟或超时会让连接卡住,轻则拖慢响应,重则耗尽连接池,整个应用数据库请求排队。
性能影响:Laravel 默认 MySQL 连接池大小通常为 10–20,一个 5 秒的 HTTP 请求就能让多个并发请求阻塞。
- 所有非数据库操作(HTTP、文件、Redis、队列推送)必须移出事务块
- 需要“事务后执行”的逻辑,用
DB::afterCommit(fn () => dispatch(new SomeJob)) - 实在要同步依赖外部结果,先查好、缓存好,再进事务;别在事务里实时 fetch
事务真正可靠的边界,永远只取决于你是否让异常透出、是否混淆了逻辑分层、是否把 IO 操作误当成原子步骤——这些地方一松动,回滚就成摆设。










