事务回滚不生效首要查表引擎是否为innodb,因myisam等引擎不支持事务,rollback会被mysql静默忽略;其次确保db::transaction()闭包内异常穿透、无try-catch吞异常,且所有操作共用同一db连接。

事务回滚不生效,先查表引擎是不是 InnoDB
MySQL 中只有 InnoDB 引擎支持事务,MyISAM、MEMORY 等引擎压根不认 ROLLBACK。ThinkPHP 或 PDO 发出的 ROLLBACK 指令会被 MySQL 静默忽略,数据早已落盘。
执行这条 SQL 确认:SHOW CREATE TABLE your_table_name;,看输出末尾是不是 ENGINE=InnoDB。不是?那就必须改:ALTER TABLE your_table_name ENGINE=InnoDB;,操作前务必整表备份。
闭包里加了 try-catch,异常被吞了
Db::transaction() 依赖异常穿透到框架内部才能触发自动回滚。一旦你在闭包里自己 try { ... } catch (\Exception $e) { ... },异常就止步于闭包内,框架完全不知道出错了,自然照常 COMMIT。
- ❌ 错误写法:在闭包里捕获并处理异常,不 re-throw
- ✅ 正确做法:闭包里不写任何
try-catch,让异常原样冒泡上去 - ⚠️ 补充:别在外层
try包住整个Db::transaction()调用——事务早已提交,外层catch毫无意义
PDO 手动事务中 DDL 语句导致隐式提交
在事务中执行 DROP TABLE、CREATE TABLE、ALTER TABLE 等 DDL 语句,MySQL 会立刻隐式提交当前事务,之后再调 rollback() 也救不回前面的 DML 操作(比如 INSERT、UPDATE)。
典型陷阱:
- 在
beginTransaction()和commit()之间写了$pdo->exec("CREATE TABLE tmp_log (...)"); - 哪怕后续抛异常、调了
rollback(),CREATE和它之前的所有变更都已生效 - 解决方案:把 DDL 操作单独拿出来,在事务外执行;或用
SET autocommit = 1显式切回自动提交模式再操作
手动事务没配对,或连接不一致
用 Db::startTrans() + Db::commit()/Db::rollback() 时,最容易踩两个坑:
- 忘记在
catch块里调Db::rollback(),或者commit()写在try外面导致无论成功失败都执行 - 跨模型或跨
Db::connect()调用,实际用了不同 PDO 连接实例——事务只在单个连接上有效,其他连接的操作根本不在同一事务上下文里 - ThinkPHP 5.1+ 起,
startTrans在非闭包模式下不绑定连接实例,推荐一律改用Db::transaction()闭包写法
最隐蔽的问题是:回滚后连接状态可能异常(比如死锁残留),后续查询直接报 PDOException: Cannot execute queries while other unbuffered queries are active——这不是事务没生效,而是连接本身坏了,得重建或重连。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











