mysqli::autocommit(false) 是开启事务的起点,它关闭自动提交模式,使后续dml语句进入隐式事务上下文,需显式commit或rollback;不依赖begin/start transaction,且要求引擎为innodb。

mysqli::autocommit(false) 是开启事务的起点
直接调用 $mysqli->autocommit(false) 是 PHP 中最常用、也最稳妥的事务开启方式。它会关闭当前连接的自动提交模式,此后所有 DML 语句(INSERT、UPDATE、DELETE)都进入一个隐式事务上下文,直到你显式调用 commit() 或 rollback()。
注意:这不是“启动一个事务块”,而是“让后续所有语句默认属于事务”——和 MySQL 命令行里执行 SET autocommit = 0 效果一致。它不依赖 BEGIN 或 START TRANSACTION,也不受存储引擎是否支持显式事务语句的影响(只要引擎本身支持事务,比如 InnoDB)。
-
autocommit(false)必须在执行任何 DML 前调用;如果中间混入了CREATE TABLE等 DDL,会触发隐式提交,导致前面的操作提前落库 - 调用后无需再写
START TRANSACTION;重复调用不会报错,但无意义 - 若连接断开且未
commit,事务自动回滚——这点常被忽略,尤其在长脚本或 CLI 模式下
为什么不用 mysqli::begin_transaction()?
PHP 7.0+ 提供了 $mysqli->begin_transaction() 方法,看起来更“标准”,但它本质只是封装了 START TRANSACTION 语句,并不改变 autocommit 状态。问题在于:如果当前 autocommit = 1,只调用 begin_transaction() 后执行一条 UPDATE,再 commit(),看似正常;但一旦中间出错没捕获,没 rollback(),下次再执行 DML 就又变成自动提交了——事务边界变得模糊。
相比之下,autocommit(false) 把控制权牢牢握在 PHP 层:事务生命周期完全由你决定,不会因某次忘记 begin_transaction() 而意外失效。
-
begin_transaction()支持传参,如MYSQLI_TRANS_START_READ_ONLY,适合只读场景,但日常增删改几乎用不到 - 如果你用的是老版本 PHP(autocommit(false) 是唯一兼容方案
- 混合使用两者(比如先
autocommit(false)再begin_transaction())不会冲突,但没必要
错误处理必须显式判断 mysqli->errno
mysqli 不会因为 SQL 执行失败就自动中断后续语句或触发回滚。哪怕 UPDATE 因主键冲突返回 false,下一行 INSERT 仍会继续执行——最终可能留下脏数据。
正确做法是每条 DML 后检查 $mysqli->errno,为 0 才继续,非 0 就立刻 rollback() 并退出逻辑。
- 别依赖
if ($mysqli->query($sql)),因为某些错误(如警告、影响行为异常)可能不中断执行但已破坏一致性 -
$mysqli->affected_rows不能替代errno判断:更新 0 行可能是条件不匹配,也可能是语句执行失败,二者含义完全不同 - 在 try/catch 中捕获异常不够——mysqli 默认不抛异常,需手动设置
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT),否则query()出错仍静默返回 false
InnoDB 表 + autocommit(false) 是硬性前提
事务不是开关,而是能力组合。两个条件缺一不可:
- 表必须是
InnoDB引擎(SHOW CREATE TABLE t查看ENGINE=InnoDB);MyISAM表调autocommit(false)完全无效,DML 依然立即生效 - 连接必须使用支持事务的用户权限;极少数受限账号可能被禁止事务操作,表现为
START TRANSACTION报错,但autocommit(false)不报错却不起作用 - 事务不跨连接:你在 CLI 开启事务修改了数据,Web 请求里查不到,这是隔离性正常表现,不是 bug
最容易被跳过的其实是引擎检查——开发环境建表时随手用 CREATE TABLE 没指定 ENGINE,默认可能是 MyISAM(尤其旧版本 MySQL),上线后事务始终不生效,排查起来非常耗时。











