thinkphp6事务回滚需同时满足三前提:表引擎为innodb、复用同一pdo连接、异常穿透至框架层,缺一即导致db::rollback()静默失效;推荐用db::transaction()闭包自动回滚,手动事务须严格try-catch配对并确保异常重抛。

ThinkPHP6 的数据库事务回滚不是靠“开启开关”实现的,而是依赖三个硬性前提是否同时满足:表引擎必须是 InnoDB、所有操作必须复用同一个 PDO 连接、异常必须穿透到事务框架层。缺一不可,否则 Db::rollback() 会静默失效——数据照写,表面成功,实际已破坏一致性。
确保表引擎为 InnoDB
MyISAM 引擎完全不支持事务,哪怕代码写了 Db::startTrans(),也不会报错,但回滚毫无作用。
- 检查当前表引擎:
SHOW CREATE TABLE user;,确认输出含ENGINE=InnoDB - 批量转换(慎用,生产环境先备份):
ALTER TABLE `user` ENGINE=InnoDB; - 建表时显式指定:
ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
用 Db::transaction() 闭包自动回滚(推荐)
这是最简洁、最安全的方式,框架会在闭包抛出未捕获异常时自动回滚,无需手动调用 rollback()。
- 所有关联操作必须写在同一个闭包内
- 闭包内不能用
try-catch吞掉异常;若需记录日志,必须throw $e重抛 - 避免混用不同连接,如
Db::connect('slave')或Db::master(),否则脱离事务上下文
示例:
Db::name('user')->where('id', $userId)->dec('balance', $amount);
$orderId = Db::name('order')->insertGetId(['user_id' => $userId, 'amount' => $amount]);
Db::name('product')->where('id', 1001)->dec('stock', 1);
return $orderId;
});
手动控制事务(startTrans + commit/rollback)
适用于需要条件判断、跨模型混合调用、或需局部处理失败逻辑的场景,但必须严格配对,且自行承担异常传播责任。
- 必须用
try-catch包裹全部操作 -
catch块中必须调用Db::rollback(),不能只记录日志就结束 - 禁止在事务中使用
dd()、dump()、exit等终止函数,会导致连接卡死 - 调试时查中间状态,应另起无事务连接:
Db::connect()->table('user')->find(1)
示例:
Db::startTrans();try {
Db::name('user')->save(['status' => 'paid']);
// 其他操作...
Db::commit();
} catch (\Exception $e) {
Db::rollback();
throw $e;
}
读不到刚插入的数据?注意连接与隔离级别
事务内 insert 后立即 select 查不到,常见于两类情况:
- 读写分离:insert 走主库,select 被路由到未同步的从库 → 改用
Db::master()->table(...)->select() - 连接切换:用了
Db::connect()新建连接 → 所有操作统一用默认连接,或显式传入Db::getConnect() - 验证事务是否生效:在闭包末尾加
throw new \Exception('test');,看数据是否消失
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











