PHP mysqli的begin_transaction与commit事务回滚失效问题
事务未生效的核心原因
MySQLi 扩展不会在单条SQL执行失败时自动回滚已开启的事务,你当前的代码没有对每条SQL的执行结果做校验,无论中间语句是否报错,最后都会强制执行commit(),自然会把之前执行成功的第一条插入语句提交落库。
另外要先确认基础前提:你操作的categories表必须使用支持事务的存储引擎(如InnoDB),如果用的是MyISAM等不支持事务的引擎,所有事务相关语句都不会生效,SQL执行后会直接持久化。
正确实现原子性事务的方案
逐次校验事务内每一条SQL的执行结果,只要任意一条执行失败,立刻调用rollback()方法撤销事务内所有已完成的操作,终止后续流程;只有所有SQL全部执行成功时,才调用commit()提交事务。
推荐开启MySQLi的异常报错模式,用try/catch结构捕获错误,避免手动判断返回值出现遗漏:
<?php // 开启MySQLi严格错误模式,SQL执行出错时直接抛出异常 mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); $connection = mysqli_connect("数据库地址", "数据库用户名", "数据库密码", "数据库名"); try { // 开启事务 $connection->begin_transaction(); // 执行第一条插入语句 mysqli_query($connection, "INSERT INTO categories (name) VALUES ('{}')"); // 执行第二条插入语句,你测试时写的错误SQL执行到这里会直接抛出异常 mysqli_query($connection, "IINSERT INTO categories (name) VALUES ('{}')"); // 所有语句执行无异常,提交事务持久化数据 $connection->commit(); echo "数据批量插入成功"; } catch (mysqli_sql_exception $e) { // 捕获到SQL执行错误,回滚事务内所有操作 $connection->rollback(); echo "插入失败,已回滚所有未提交操作,错误原因:" . $e->getMessage(); } ?>
注意事项
- 事务内不要执行CREATE、ALTER、DROP这类DDL语句,这类语句会触发隐式提交,导致回滚失效。
- 事务的开启、语句执行、提交/回滚必须在同一个数据库连接实例中完成,跨连接的操作不属于同一个事务上下文,无法保证原子性。
- 如果不使用异常模式,必须在每次调用
mysqli_query()后判断返回值是否为false,一旦返回false立刻执行回滚,不能继续执行后续语句也不能直接走提交逻辑。
内容的提问来源于stack exchange,提问作者Taha Sami
相关产品推荐
相关产品推荐

