Laravel 9 与 11 的隐式事务(DB::transaction)和手动事务(beginTransaction/commit/rollback)写法、行为逻辑及回滚机制完全一致,仅控制粒度不同:前者自动管理提交/回滚,后者需显式 try-catch 并调用 rollback;两者均依赖 PDO 异常模式,回滚仅作用于当前连接未提交变更,不跨连接、不保护模型事件副作用,嵌套事务无效。

隐式事务和手动事务在 Laravel 9 与 Laravel 11 中的写法、行为逻辑和回滚机制完全一致,框架未在两版本间调整事务底层实现。区别仅在于使用方式和控制粒度,而非功能支持或语义变化。
隐式事务:DB::transaction() 闭包方式
这是最简洁、推荐用于多数业务场景的方式,由 Laravel 自动管理 begin / commit / rollback。
- 事务成功时自动提交,抛出未捕获的 Exception 或 Throwable 时自动回滚;仅 return false、dd()、exit() 不触发回滚
- 所有数据库操作必须写在闭包内,外部调用(如模型 save() 后再进闭包)会脱离事务上下文
- 支持返回值:
$user = DB::transaction(fn() => User::create([...])),$user 即创建结果 - 可设重试次数:
DB::transaction(..., 3),适用于瞬时数据库抖动,但不解决逻辑冲突 - 依赖 PDO 异常模式:需确保数据库配置中开启
PDO::ERRMODE_EXCEPTION,否则 SQL 错误可能静默失败
手动事务:beginTransaction / commit / rollback
适合需要精细控制流程、插入非数据库操作或处理复杂异常分支的场景,但容错性低,易遗漏回滚。
- 必须配 try-catch 结构,且 catch 中要显式调用
DB::rollback()并重新 throw 异常,否则事务悬挂 - 执行
DB::rollback()前建议检查DB::transactionLevel() > 0,防止在无事务时调用报错 - 外部操作(如 HTTP 请求、发邮件、文件写入)必须放在
commit()之后——它们无法随事务回滚,提前执行会导致状态错位 - 若需局部回滚(如子流程失败不影响主流程),需手动结合
DB::rollBackTo('sp_name')与保存点,Laravel 不自动创建 savepoint
回滚机制的关键共性(9 与 11 完全相同)
两个版本均不区分“隐式”或“手动”来改变回滚本质:回滚始终是连接级别的 ROLLBACK 操作,只影响当前数据库连接上的未提交变更。
- 事务不跨连接:
DB::connection('mysql')和DB::connection('pgsql')的事务彼此隔离,不能联动提交或回滚 - 模型事件(created、updated)和观察者中的副作用(如日志、通知)不受事务保护,需改用
afterCommit()或事件监听器实现最终一致性 - 视图不可写:对数据库视图执行 insert/update 必然失败,写操作必须指向基础表,并在 Eloquent 模型中明确指定
protected $table = 'real_table' - 嵌套事务无效:多次调用
DB::transaction()只增加嵌套计数,底层仍是单层 BEGIN;内层异常被外层 catch 住,变更即提交,不会回滚
常见陷阱与规避建议
无论 Laravel 9 还是 11,以下问题表现一致,生产环境需重点防范:
- 在事务中调用阻塞型外部服务(如同步 API、大文件处理),易触发 MySQL
innodb_lock_wait_timeout,导致连接中断、事务残留 - 忘记在手动事务中 throw 异常,使错误被吞掉,后续请求复用该连接时可能复用未清理的事务状态
- upsert() 冲突检测依赖唯一索引,缺失索引时部分数据插入成功、部分失败,事务仍会提交已成功部分
- 审计日志等强一致性要求的操作,必须与业务逻辑同处一个
DB::transaction()闭包,不能靠模型事件异步落库


















