PHP MySQL 与 MySQLi 事务处理详解及注意事项
PHP 中的数据库操作常涉及多步执行的业务逻辑,此时需要利用事务保证数据一致性。本文将深入对比原生 mysql 函数与 mysqli 对象在事务处理上的实现方式,剖析自动提交(autocommit)机制陷阱,并解释为何某些 SQL 语句无法被回滚。同时,针对默认不支持事务的存储引擎,提供启用 InnoDB 支持的具体配置方法。
为什么需要使用事务
在数据库操作中,多条 SQL 语句往往具有逻辑关联。例如转账或积分更新,若其中一步失败,后续步骤不应执行,且已执行的步骤需撤销,以保证数据状态的完整性。这就是事务(Transaction)的核心作用:确保一组操作要么全部成功,要么全部失败。
需要注意的是,MySQL 默认的存储行为是在每条 SQL 语句执行后自动提交(Commit)。这意味着每条语句独立成为一个事务。为了实现多步操作的原子性,必须手动关闭自动提交模式或使用显式的事务启动语句。
准备条件:存储引擎的选择
在使用事务前,必须确认使用的数据表是否支持事务。MySQL 的不同存储引擎对事务的支持情况如下:
- InnoDB:支持 ACID 事务、行级锁和高并发,是 Web 应用中最常用的支持事务的引擎。
- BDB:Berkeley DB,也支持事务,但在现代 MySQL版本中较少使用。
- MyISAM:不支持事务。它主要用于只读程序以提高性能。如果数据库默认引擎是 MyISAM,则需要手动切换为 InnoDB。
如何开启 InnoDB 支持:部分服务器安装时可能禁用了 InnoDB。若发现不支持事务,可尝试以下步骤:
- 编辑 MySQL 配置文件(如
/etc/mysql/my.cnf或 Windows 下的my.ini),注释掉或移除skip-innodb参数。 - 重启 MySQL 服务。
- 通过 phpMyAdmin 或命令行执行
SHOW ENGINES;,确认 InnoDB 状态为 YES。
Mysqli 方式处理事务
MySQLi 提供了面向对象的方式封装了事务操作,使用 autocommit(false) 开始事务,配合 commit() 和 rollback() 完成或回滚。这种方式结构清晰,错误判断通过 $mysqli->errno 进行。
第1步:初始化连接并关闭自动提交
建立数据库连接后,调用 autocommit(false) 禁止每条语句自动提交,从而允许我们将多个查询组合在一个事务中。
$mysqli = new mysqli('localhost', 'root', '', 'DB_Lib2Test');
$mysqli->autocommit(false); // 开始事务,关闭自动提交
第2步:执行业务 SQL 语句
在此阶段执行所有预期的数据库操作,如更新、插入等。
$sql1 = "UPDATE User SET ScoreCount = ScoreCount + 10 WHERE ID='123456'";
$sql2 = "UPDATE ScoreDetail SET FScore = 300 WHERE ID='123456'";
第3步:检查错误并提交或回滚
执行完毕后,检查 $mysqli->errno。如果无错误(值为 0),则提交事务;否则回滚。
$mysqli->query($sql1);
$mysqli->query($sql2);
if (!$mysqli->errno) {
$mysqli->commit();
echo 'Success';
} else {
echo 'Error';
$mysqli->rollback();
}
PHP mysql_* 系列函数处理事务
虽然 mysql_* 系列函数已被废弃且不建议在新项目中使用,但了解其事务实现原理有助于理解底层机制。该系列函数通过发送 SQL 指令 'start transaction' 或 'SET autocommit=0' 来手动控制事务。
以下示例展示了如何使用 mysql_query 发起事务:
$conn = mysql_connect('localhost', 'root', '');
mysql_select_db('DB_Lib2Test');
// 方法1: 显式启动事务
mysql_query('START TRANSACTION');
// 方法2: 关闭自动提交
// mysql_query('SET autocommit=0');
$sql1 = "UPDATE User SET ScoreCount = ScoreCount + 10 WHERE ID='123456'";
$sql2 = "UPDATE ScoreDetail SET FScore = 300 WHERE ID='123456'";
mysql_query($sql1);
mysql_query($sql2);
if (mysql_errno()) {
mysql_query('ROLLBACK');
echo 'Error';
} else {
mysql_query('COMMIT');
echo 'Success';
}
// 恢复自动提交状态
// mysql_query('SET autocommit=1');
注意事项:自动提交的陷阱
如果在执行事务期间,意外执行了一条未包含在事务块内的 SQL(例如直接执行了一个插入语句),且没有关闭自动提交,该语句会立即生效并不可回滚。因此,在使用事务时,务必确保相关 SQL 都在同一事务上下文中执行,或者在必要时显式设置 SET autocommit=0。
特殊现象:DDL 语句与事务回滚
在进行复杂的数据迁移或表结构调整时,开发者常遇到“事务似乎失效”的情况。这是因为 DDL(数据定义语言)语句会自动触发 COMMIT,导致之前未提交的操作提前结束。
以下 DDL 语句在执行时会隐式提交当前事务:
ALTER TABLE,CREATE TABLE,DROP TABLERENAME TABLETRUNCATE TABLELOCK TABLES,UNLOCK TABLESSET AUTOCOMMIT=1START TRANSACTION(本身也是一个提交触发点)
案例分析:Rename 导致的隐式提交
假设我们尝试通过创建新表并重命名的方式安全地修改表结构,并将这些操作包裹在事务中:
$mysqli = new mysqli('localhost', 'root', '', 'DB_Lib2Test');
$mysqli->autocommit(false);
$sql1 = 'CREATE TABLE ScoreDetail_new (id INT)';
$sql2 = 'RENAME TABLE ScoreDetail TO ScoreDetail_bak';
$sql3 = 'RENAME TABLE ScoreDetail_new TO ScoreDetail';
$mysqli->query($sql1);
$mysqli->query($sql2);
// 如果此处模拟一个错误,或者继续执行...
$mysqli->query($sql3);
if (!$mysqli->errno) {
$mysqli->commit();
echo 'Success';
} else {
echo 'Error';
$mysqli->rollback();
}
在上述代码中,即便 $sql2 失败或未执行,$sql1 创建的表依然存在于数据库中。这是因为 RENAME TABLE 在执行时,MySQL 会先自动提交之前未完成的操作(包括 CREATE TABLE),然后再执行重命名。因此,DDL 语句不能参与普通的事务回滚。在设计依赖事务保护的安全方案时,应尽量避免混合使用 DDL 和 DML,或使用存储过程及临时表策略规避此问题。
简要总结
在处理 PHP 数据库操作时,推荐使用 MySQLi 或 PDO 进行事务管理。务必确认使用的是 InnoDB 引擎,因为 MyISAM 不支持事务。同时,要警惕 autocommit 默认开启带来的副作用,以及 DDL 语句引发的隐式提交风险,合理规划 SQL 执行顺序以确保数据的一致性。
以上就是 PHP MySQL与MySQLi事务处理及相关注意事项的详细内容,更多关于PHP数据库操作优化的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









