发布于2026-07-08 阅读(0)
扫一扫,手机访问
说个现实情况,很多人在项目里写了事务代码,结果数据该写还是写进去了——这不是框架有问题,而是几个细节没到位。ThinkPHP 的 MySQL 事务能不能用?能用。但默认状态下它可能根本没生效。核心就卡在三处:数据库引擎是不是 InnoDB、自动提交有没有关、异常有没有被正确捕获并触发回滚。一个没注意,事务就是个摆设。
这种方式把 startTrans()、commit() 和 rollback() 全封装好了。闭包一跑,只要内部抛异常,事务自动回滚;跑完了没出问题,自动提交。代码写起来干净,不容易漏掉某个清理步骤。
不过有几个细节不能踩:
throw new \Exception 或者出现未捕获错误,事务就会回滚,不需要自己写 try-catch举个例子就清楚了:
Db::transaction(function () {
Db::table('order')->insert(['uid' => 100, 'status' => 'created']);
Db::table('stock')->where('id', 1)->dec('num', 1);
// 这里有个常见的坑:dec 返回 false 不会让闭包中断
// 必须自己检查库存,如果变成负数,手动抛异常
if (Db::table('stock')->where('id', 1)->value('num') < 0) {
throw new \Exception('库存不足');
}
});
看到没有?判断必须自己做,false 不会自动触发回滚——你期待的“如果减失败了就回滚”,框架并不会替你判断。
有些人觉得闭包不够灵活,喜欢手动操作。从 startTrans() 到 commit() 再到 rollback(),全自己管。但这套手动流程,往往就断在哪几个环节?
Db::startTrans() 必须在所有操作之前调用,而且只能调一次。重复调用不报错,但也不会生效delete()、update() 成功返回影响行数,失败是 false 或抛异常,不能只靠一个 if 判断完事if ($a && $b) 这样的链式检查,因为部分 SQL 语法错误(比如字段不存在)会直接抛异常,不会被 if 捕获Db::rollback()。PHP 异常没被捕获时,事务不会自动回滚,它会留在那里等超时很多回滚失败的问题,根本不在代码层面,而是数据库层或连接配置的锅。来,直接列出来对照检查:
SHOW CREATE TABLE user,确认输出里是 ENGINE=InnoDB。老项目迁移时,字典表、日志表这类容易被漏改Db::connect()->getPdo()->getAttribute(PDO::ATTR_AUTOCOMMIT) 查一下,应该是 0 而不是 1rollback(),它也纹丝不动ALTER TABLE)、或者调用了存储过程,这些操作会触发隐式提交,事务会被提前切断innodb_lock_wait_timeout 默认 50 秒,长事务很容易触到,但 PHP 这边 catch 不到,你以为还在事务里,其实已经断了事务本身不解决并发冲突,它只是给了你一块“临时保护区”。如果并发场景没设计好,事务反而会把问题放大。几个要点:
user 和 order 表,必须固定先更新 user 再更新 order,否则很容易死锁UPDATE ... WHERE status = 'pending' 可能锁住大量行UPDATE account SET balance = balance - 100 WHERE id = 1 AND version = 5,失败了就重试
说到底,真正难的不是写 Db::transaction(),而是确认每张表都是 InnoDB、每次操作都落在同一个连接、每个失败分支都走到了 rollback()。这些细节漏掉一个,事务就是纸老虎。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8