开启事务前必须调用$mysqli->autocommit(false),否则START TRANSACTION和COMMIT无效;需检查errno及时rollback;避免混用MyISAM与InnoDB表;控制事务时长防锁表和超时。

mysqli 开启事务前必须关掉自动提交
不关 autocommit,START TRANSACTION 和 COMMIT 全部无效——这是最常踩的坑。mysqli 默认开启自动提交,每条 SQL 执行完立刻落盘,事务根本没机会生效。
实操建议:
- 连接建立后第一件事就是调用
$mysqli->autocommit(false) - 别依赖
mysqli_query($mysqli, "SET autocommit = 0"),它在某些版本或配置下不可靠 - 如果用的是面向对象风格,确认不是写成
$mysqli->autocommit = false(这是错的,autocommit是方法,不是属性) - 事务结束后,建议显式恢复:
$mysqli->autocommit(true),避免后续查询意外被卷入未关闭的事务
rollback 的触发时机和常见失效原因
rollback() 不是“只要调用了就一定回滚”,它只对当前活跃事务起作用;一旦事务已提交、已回滚、或连接断开,再调用就静默失败,且不报错。
常见错误现象:
立即学习“PHP免费学习笔记(深入)”;
- 执行了
commit()后又调rollback()→ 没效果,但代码不报错 - 事务中某条语句出错(比如主键冲突),但没检查
mysqli_errno()就继续往下走 → 最终commit()把部分成功操作提交了 - 用
mysqli_real_escape_string()处理数据时传入了错误的连接句柄 → 导致后续查询失败却没被捕获
正确做法:所有 DML 操作后都检查 $mysqli->errno,非零就立即 rollback() 并退出。
事务里不能混用 MyISAM 和 InnoDB 表
MyISAM 不支持事务,哪怕你开了事务、执行了 INSERT INTO myisam_table,这条语句也会立刻生效,rollback() 对它完全无效。
使用场景中容易忽略的点:
- 建表时没指定引擎,默认可能是 MyISAM(尤其老项目或共享主机)
- 迁移过程中部分表改成了 InnoDB,但关联的字典表仍是 MyISAM
- 用
CREATE TABLE ... SELECT复制数据,新表会继承源表引擎,可能意外生成 MyISAM 表
检查方式:SHOW CREATE TABLE <code>table_name,确认输出里有 ENGINE=InnoDB。
长事务导致锁表和超时,怎么控制边界
事务持续时间越长,行锁/表锁持有越久,其他请求阻塞越明显;MySQL 的 wait_timeout 和 innodb_lock_wait_timeout 也会让事务中途被杀,但 PHP 层未必感知到。
性能与兼容性影响:
- 避免在事务里做 curl 请求、文件读写、sleep() 等耗时操作
- 不要把用户输入校验、日志记录、消息队列投递放在事务内
- 如果必须分步处理,考虑用「两阶段提交」思路:先写状态为
pending,事务提交后再异步完成后续动作 - 用
$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 3)和mysqli_report(MYSQLI_REPORT_STRICT)提前暴露连接/超时问题
真正麻烦的不是语法写错,而是事务边界划在了业务逻辑缝里——那里既看不见锁,也测不出超时,只有线上卡住时才冒出来。











