thinkphp6事务需同时满足innodb引擎、异常穿透、同一pdo连接三条件,缺一不可;否则db::rollback()无效。

ThinkPHP6 的数据库事务不是靠配置开关启用的,而是靠代码调用决定是否开启、如何回滚。所谓“配置”,实际是指确保底层环境和调用方式满足事务生效的前提——否则写再多 Db::transaction() 也没用。
表引擎必须是 InnoDB
MyISAM 引擎完全不支持事务,哪怕代码里写了 Db::startTrans(),执行时也不会报错,但 rollback() 什么都不会做,数据照常写入。
- 检查方式:
SHOW CREATE TABLE user;看输出里是不是ENGINE=InnoDB - 批量转换命令:
ALTER TABLE `user` ENGINE=InnoDB; - 建表时显式指定:
ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
Db::transaction() 闭包里异常不能被吞掉
这是最常见、最隐蔽的回滚失效原因:闭包内抛了异常,但被 try-catch 捕获后没重新 throw,框架就认为操作成功,自动 commit。
- 错误写法:
Db::transaction(function () { try { Db::table('user')->insert([...]); } catch (\Exception $e) { Log::error($e); } });→ 数据已提交 - 正确写法(让异常穿透):
Db::transaction(function () { Db::table('user')->insert([...]); throw new \Exception('test'); }); - 需要自定义处理?改用手动模式:
Db::startTrans()+try/catch+ 显式Db::rollback()
跨模型或跨 Db::connect() 调用会丢失事务上下文
事务绑定在 PDO 连接实例上。如果闭包里混用 UserModel::create() 和 Db::connect('slave')->table(...),后者根本不在同一连接,插入/更新不会参与事务。
- 确认是否同连接:
var_dump(Db::getPdo());在不同位置执行,输出对象 ID 应一致 - 读写分离场景下查刚插入的数据失败?强制走主库:
Db::master()->table('user')->select() - 模型想复用事务?别改
$model->connection,统一用默认连接;或显式传入连接实例:new UserModel(['connection' => Db::getConnect()])
调试时千万别用 dd()、dump() 或 exit
这些函数会直接终止 PHP 执行,PDO 连接卡在 “transaction active” 状态,后续请求复用该连接时,Db::rollback() 会报 There is no active transaction,甚至整个连接池雪崩。
- 替代方案:用
Log::info()记日志,配合Db::getLastSql()看生成语句 - 真要中断看状态?在事务外另起连接查库:
Db::connect()->table('user')->where('id', 1)->find(); - 本地测试可加 guard:
if (app()->isDebug()) { Db::rollback(); }防止连接滞留
事务不是“写了就兜底”的保险丝,它依赖异常传播路径、连接一致性、引擎支持三者同时成立。漏掉任意一环,rollback() 就只是个不会响的空按钮。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











