发布于2026-07-03 阅读(0)
扫一扫,手机访问
先说一个很多人踩过的坑——不管是 mysqli 还是 PDO,默认情况下都不开启事务行为。也就是说,你不显式关闭自动提交、不调用开始事务的方法,那所有 SQL 执行完就立刻落库,这时候你写 rollback() 是没有任何效果的。很多人折腾半天回滚没反应,问题就出在这里。
mysqli 的事务,关键在于先关掉自动提交很多开发者会问:我明明调了 begin_transaction(),怎么回滚还是没生效?根本原因在于 mysqli 默认就是自动提交模式。begin_transaction() 其实只是“声明要开一个事务”,但如果 autocommit 仍然为 true,那每条 query() 执行后还是会立刻写入数据库。
正确的做法是:
$mysqli->autocommit(false)begin_transaction()(PHP 7.0+ 引入)比手写 START TRANSACTION 更安全,可读性也好很多commit() 或 rollback() 之后,建议手动把 autocommit 恢复为 true,否则会影响后面那些独立的查询multi_query(),必须配合 next_result() 逐条检查每条语句的执行状态,光看第一条返回值是不够的一个典型的安全写法是这样的:
$mysqli = new mysqli($host, $user, $pass, $db);
$mysqli->autocommit(false);
try {
$mysqli->begin_transaction();
$mysqli->query("UPDATE accounts SET balance = balance - 100 WHERE id = 1");
$mysqli->query("UPDATE accounts SET balance = balance + 100 WHERE id = 2");
$mysqli->commit();
} catch (Exception $e) {
$mysqli->rollback();
throw $e;
} finally {
$mysqli->autocommit(true);
}
PDO 的事务失效,九成是因为错误模式没设对PDO 默认的错误模式是 PDO::ERRMODE_SILENT,这意味着 SQL 执行报错时,它只返回 false,不会抛异常。后果就是 catch 块根本抓不到任何错误,rollback() 也就永远不会被执行。这是最常见的问题,没有之一。
解决方案很明确:
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTIONPDO::ATTR_AUTOCOMMIT => false,不要依赖 beginTransaction() 的隐式开关commit() 和 rollback() 执行后会自动把 autocommit 重置为 true。所以如果后面还有连续的事务,必须重复调用 beginTransaction()PDO 实例来管理同一个事务,跨对象调用 rollback() 是无效的一个标准配置示例:
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_AUTOCOMMIT => false,
]);
SA VEPOINT,别指望“嵌套事务”MySQL 本身不支持真正的嵌套事务。如果你连续多次调用 beginTransaction(),后面的调用要么被忽略,要么直接报错。想要实现“子事务回滚不影响父事务”的效果,只能靠保存点来模拟。
$pdo->exec("SA VEPOINT sp1") 来设置一个保存点$pdo->exec("ROLLBACK TO SA VEPOINT sp1") 回滚到该点commit 了,内层再去 ROLLBACK TO 肯定会失败RELEASE SA VEPOINT sp1 可以显式释放保存点,但不是必需的。事务结束时,系统会自动清理所有保存点必须提醒的是,保存点不是万能的。DDL 语句(比如 ALTER TABLE)会隐式提交当前事务,导致所有保存点失效。这一点在实际开发中很容易踩坑。
commit/rollback 远远不够拿库存扣减来举例:两个请求同时读到“剩余 10”,都判断可以扣减,结果就超卖了。事务本身并不解决“读-改-写”这个过程中的竞态问题,需要额外的保护措施。
SELECT ... FOR UPDATE 加行锁——但要注意,这只对 InnoDB 有效,而且必须在事务内部使用WHERE qty >= ?,这样第二条 UPDATE 的影响行数就会是 0,你就可以据此判断发生了并发冲突最后还有一个容易被忽略的点:事务必须在单次 HTTP 请求的生命周期内完成闭环。PHP-FPM 的请求结束后,连接虽然可能被复用,但事务状态不会跨请求延续。千万别指望“前端分步提交、后端分步事务”这种方案,那基本是给自己挖坑。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8