事务没生效主因是异常未透出闭包或数据库引擎非innodb:db::transaction()内需让异常穿透触发回滚,myisam引擎完全忽略事务指令,嵌套调用不产生子事务,查不到刚插入数据多因连接未复用。

事务没生效,大概率不是 Laravel 的问题,而是你没让异常透出闭包,或者数据库引擎压根不支持事务。
DB::transaction() 里异常没抛出来,事务就不回滚
这是最常踩的坑:写了 DB::transaction(),但里面用了 try/catch 吞掉异常,或用 return false 提前退出,结果第一条 SQL 写进去了,第二条失败了,数据就脏了。
- 闭包内必须让异常“穿透出去”,Laravel 才会触发回滚;
catch里处理完日志后得throw $e - 别用
if (! $user->save()) { return; }这类软退出——它不报错,事务照常提交 - 业务校验失败时,要主动
throw new RuntimeException('库存不足'),而不是静默返回 - 模型的
save()返回false不等于抛异常,得自己判断并 throw
MySQL 引擎不是 InnoDB,事务完全无效
DB::transaction() 在 MyISAM 表上运行毫无意义——它不会报错,也不会回滚,就像没加事务一样。Laravel 不检查引擎类型,只管发 BEGIN 和 COMMIT,而 MyISAM 忽略这些指令。
- 用
SHOW CREATE TABLE orders确认表引擎,必须是ENGINE=InnoDB - 建表迁移里显式指定:
$table->engine = 'InnoDB'; - 如果用的是云数据库(如阿里云 RDS),有些默认模板仍可能配成 MyISAM,不能只信文档
嵌套调用 DB::transaction() 不等于嵌套事务
你在服务 A 里调了 DB::transaction(),A 又调了服务 B,B 也写了 DB::transaction()——这不会创建两个事务,B 的调用只是增加事务层级计数,commit 或 rollback 仍由最外层控制。
- 内层
DB::transaction()里的rollback会报错:There is no active transaction - 真需要局部回滚,得手动用
DB::statement('SAVEPOINT sp1')+DB::statement('ROLLBACK TO SAVEPOINT sp1') - PDO 预处理模式下,
SAVEPOINT可能失败,需关掉模拟预处理:'options' => [PDO::ATTR_EMULATE_PREPARES => false] - 更稳妥的做法是把事务边界提到最外层,B 方法只做纯 SQL 或模型操作,不碰事务控制
事务里查不到刚插入的数据?多半是连接没复用
在 DB::transaction() 闭包里用 User::create() 插了一条,紧接着用 User::find() 查不到,常见原因是:你显式指定了连接名,或触发了多连接场景。
- Laravel 默认复用同一 PDO 连接,但
DB::connection('mysql2')->table(...)会开新连接,看不到未提交数据 - Eloquent 模型没显式指定连接时,走默认配置;一旦某处用了
on('mysql2'),整个链路就断了 - 队列任务、HTTP 请求、Redis Pub/Sub 回调这些 IO 操作,必然走新连接,事务内生成的 ID 或状态不能直接传给它们
- 需要异步执行时,改用
dispatchNow(),或把关键 ID、状态作为参数传入,别依赖事务内“刚写的数据”
事务不是万能胶,它只管当前 PDO 连接上的 SQL 执行一致性;外部调用、模型事件、文件写入、缓存更新这些,都不受保护——得靠设计补位,比如幂等接口、补偿任务、延迟发布事件。











