pdo事务不回滚的主因是未显式调用rollback()或autocommit未正确关闭;需确保表引擎为innodb、异常时必调rollback()、启用异常模式或检查其返回值,并避免依赖自动回滚。

PHP PDO 事务不回滚?检查 setAttribute 是否禁用了自动提交
默认情况下,PDO 连接是开启自动提交(autocommit)的,这意味着每条 SQL 执行完立刻生效,beginTransaction() 后若没显式 commit() 或 rollback(),连接关闭时不会自动回滚——它只是被忽略。常见现象是:明明调了 rollback(),但数据还在库里。
-
$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, false)不是必须的,但必须确保没在别处意外设为true - 更稳妥的做法是:每次事务前用
$pdo->beginTransaction(),它会自动关闭 autocommit;事务结束后无论成功失败,都应显式调用commit()或rollback() - 注意:MySQL 的 MyISAM 表不支持事务,务必确认表引擎是 InnoDB
捕获异常后忘记 rollback(),导致事务悬挂
很多人只在 try 块里写 commit(),却在 catch 里漏掉 rollback(),或者用了 die()/exit() 直接中断脚本,连接断开但事务未清理,可能锁表或造成数据不一致。
- 必须把
rollback()放进 catch 块,且放在所有清理逻辑之前 - 不要依赖“脚本结束自动回滚”——PDO 不保证这点,尤其长连接或连接池场景下更危险
- 示例片段:
try { $pdo->beginTransaction(); $pdo->exec("INSERT INTO orders ..."); $pdo->exec("UPDATE inventory ..."); $pdo->commit(); } catch (Exception $e) { $pdo->rollback(); // 这行不能少 throw $e; }
PDO::rollBack() 失败却没报错?检查返回值和错误模式
rollback() 调用本身可能失败(比如连接已断、事务已提交),但默认不抛异常,返回 false。如果没检查返回值,你以为回滚了,其实没成功。
- 启用异常模式:
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION),这样rollback()失败会直接抛出PDOException - 如果不启异常模式,务必检查返回值:
if (!$pdo->rollback()) { /* 记录日志或告警 */ } - 注意:在已经 commit 或 rollback 过的事务上调用
rollback(),PDO 会返回false,不是 bug,是预期行为
多个 beginTransaction() 嵌套?PDO 不支持真正的嵌套事务
PDO 没有 savepoint 管理机制,连续调两次 beginTransaction() 不会创建子事务,第二次调用只是覆盖状态,最终一次 rollback() 会回滚全部操作——这容易让人误以为“内层事务独立”,实际并非如此。
立即学习“PHP免费学习笔记(深入)”;
- 需要局部回滚,必须手动用 MySQL 的
SAVEPOINT:$pdo->exec("SAVEPOINT sp1"); // ... 可能出错的操作 $pdo->exec("ROLLBACK TO SAVEPOINT sp1"); - PDO 本身不封装 savepoint,所有 savepoint 相关语句都得手写
exec() - 嵌套事务逻辑复杂时,优先考虑拆成独立事务,或用应用层状态控制,而非依赖数据库嵌套
事务真正难的不是语法,是判断哪段逻辑必须原子执行、哪些错误必须触发回滚、以及连接生命周期里事务状态是否始终可控。尤其是异步调用、长事务、或复用连接池时,rollback() 被跳过或静默失败,比语法写错更难排查。











