先查表引擎是否为innodb,若为myisam则事务完全无效;确保db::transaction()闭包内异常穿透、所有操作共用同一连接,否则回滚不生效。

Db::transaction() 闭包里抛异常不回滚?先查表引擎
事务回滚失败,八成不是代码写错了,而是 user 表压根不支持事务。执行 SHOW CREATE TABLE user;,如果输出里是 ENGINE=MyISAM,那 Db::transaction() 就是空转——不报错、不生效、rollback() 调了也白调。
必须改成 InnoDB:
- 单表转换:
ALTER TABLE `user` ENGINE=InnoDB; - 建表时显式声明:
CREATE TABLE `user` (...) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;(仅在配置文件设默认引擎不可靠) - 批量检查所有业务表:
SELECT table_name, engine FROM information_schema.tables WHERE table_schema = 'your_db_name' AND engine != 'InnoDB';
PDO 批量操作中 rollback() 不触发?检查异常捕获范围
$pdo->rollback() 只在有活跃事务时才起作用。常见失效场景:
- 没开事务就直接
rollback()→ 抛PDOException:“There is no active transaction” - 只 catch
\Exception,但 PDO 在 PHP 8.0+ 可能抛\Throwable(如致命错误、ParseError)→ 异常被吞,事务自动提交 - 事务中执行了 DDL 语句(如
DROP TABLE、ALTER TABLE)→ MySQL 自动隐式提交,后续rollback()对已提交部分无效
正确写法:
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
$pdo->beginTransaction();
$pdo->exec("UPDATE accounts SET balance = balance - 100 WHERE id = 1");
$pdo->exec("UPDATE accounts SET balance = balance + 100 WHERE id = 2");
$pdo->commit();
} catch (\Throwable $e) {
if ($pdo->inTransaction()) {
$pdo->rollback();
}
throw $e;
}
ThinkPHP 手动事务多模型回滚失败?每个模型都得独立控制
用 ModelA::startTrans() + ModelB::startTrans() 启动事务时,不能只对一个模型调 commit() 或 rollback()。
- 每个模型对应独立数据库连接(即使同库),
ModelA::rollback()不会影响ModelB的事务状态 - 漏掉任一模型的
rollback(),就会出现“部分写入、部分回滚”的脏数据 - PHP 8+ 下,
catch (\Exception $e)无法捕获PDOException(它继承自\Throwable,但不继承\Exception)→ 必须用catch (\Throwable $e)
关键动作必须成对出现:
OrderModel::startTrans();
LogModel::startTrans();
try {
OrderModel::create([...]);
LogModel::insert([...]);
OrderModel::commit();
LogModel::commit();
} catch (\Throwable $e) {
OrderModel::rollback();
LogModel::rollback();
throw $e;
}
mysqli 批量事务里 rollback() 没反应?确认是否关了 autocommit
mysqli_rollback($conn) 失效,大概率是因为没关自动提交。MySQL 默认是 autocommit=1,每条 SQL 都立即生效,rollback() 找不到可回滚的事务边界。
- 必须先调
mysqli_autocommit($conn, false)或mysqli_begin_transaction($conn) - 不要混用:用了
mysqli_begin_transaction()就别再手动设autocommit=false,否则行为未定义 - 注意
mysqli_query($conn, 'BEGIN')是兼容写法,但不如mysqli_begin_transaction()明确
最小可靠流程:
mysqli_begin_transaction($conn);
$result1 = mysqli_query($conn, "UPDATE t1 SET v = v + 1 WHERE id = 1");
$result2 = mysqli_query($conn, "UPDATE t2 SET v = v - 1 WHERE id = 1");
if ($result1 && $result2) {
mysqli_commit($conn);
} else {
mysqli_rollback($conn); // 此时才真正起作用
}
事务回滚不是“写了 rollback 就完事”,它依赖底层引擎、连接状态、异常传播路径三者同时在线。最容易被忽略的是:DDL 语句会偷偷提交事务,而跨模型手动事务必须每个模型都 rollback —— 少一个,数据就不一致。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











