PHP中事务不会自动回滚,必须在catch块中显式调用rollback();PDO可静默忽略非事务状态下的rollback,MySQLi则需先用begin_transaction()或START TRANSACTION开启事务。

PHP中事务自动回滚依赖try-catch显式控制
PHP的PDO或MySQLi本身不会在异常发生时自动回滚事务——必须手动调用rollback(),否则事务会一直挂起,直到连接关闭(可能造成锁表或数据不一致)。自动回滚不是语言特性,而是开发者需构建的防御逻辑。
常见错误现象:PDOException抛出后没捕获,脚本终止但commit()未执行,事务既没提交也没回滚;或只在catch里echo错误却忘了rollback()。
- 使用场景:所有涉及多条写操作且要求原子性的业务,如订单创建(扣库存+生成订单+记录日志)
- 必须在
beginTransaction()之后、commit()之前包裹完整业务逻辑 - 确保
catch块中调用rollback(),且该调用本身不应再抛出新异常(可加try-catch包裹或检查inTransaction())
用PDO实现带自动回滚的事务模板
以下是最小可靠模式,覆盖了连接异常、SQL错误、业务逻辑异常三类情况:
$pdo->beginTransaction();
try {
$pdo->exec("UPDATE stock SET qty = qty - 1 WHERE id = 1");
$pdo->exec("INSERT INTO orders (user_id) VALUES (123)");
$pdo->exec("INSERT INTO logs (msg) VALUES ('order_created')");
<pre class="brush:php;toolbar:false;">$pdo->commit();} catch (Exception $e) { $pdo->rollback(); throw $e; // 重新抛出,让上层决定如何处理 }
注意:rollback()调用前无需判断是否仍在事务中,PDO会静默忽略非事务状态下的调用;但若想更严谨,可用$pdo->inTransaction()做前置检查。
立即学习“PHP免费学习笔记(深入)”;
MySQLi不支持自动回滚,必须显式调用rollback()
MySQLi面向对象风格下,mysqli::autocommit(false)仅关闭自动提交,并不开启事务上下文——它只是为后续commit()/rollback()铺路。很多开发者误以为设了autocommit(false)就等于“进了事务”,其实不然。
-
begin_transaction()(PHP 7.0+)或query('START TRANSACTION')才是开启事务的正确方式 -
rollback()必须显式调用,MySQLi不会因异常自动触发 - 兼容性提示:PHP query('ROLLBACK')代替
rollback()方法
嵌套事务和框架封装的陷阱
原生PHP不支持真正的嵌套事务(MySQL本身也不支持),所谓“嵌套”只是保存点(savepoint)模拟。Laravel等框架的DB::transaction()底层仍基于单层事务+异常捕获,但若在事务内手动调用DB::commit()或DB::rollback(),会破坏框架的自动管理逻辑。
容易被忽略的点:事务生命周期绑定数据库连接,而非PHP请求周期。如果使用长连接(如PHP-FPM + persistent PDO),未正确回滚的事务可能污染后续请求;Swoole协程环境下更要确保每个协程持有独立PDO实例,否则rollback()可能作用于其他协程的事务。



















