PHP数据库事务怎么处理_PHP事务处理方法与使用实例
在实际的PHP项目开发中,我们经常需要同时执行多个数据库操作,比如转账场景中,要同时从转出账户扣钱,再向转入账户加钱。如果这两个操作中间出现意外,就可能导致数据不一致,这时候就需要用到数据库事务来保证操作的原子性。下面我们就来详细讲解PHP中事务的处理方法,并结合实例说明具体用法。
什么是数据库事务
数据库事务是一组数据库操作的集合,这些操作要么全部执行成功,要么全部不执行,不会出现部分执行的情况。事务具备四个核心特性,也就是常说的ACID:
- 原子性(Atomicity):事务中的所有操作是一个不可分割的整体,要么全部完成,要么全部回滚,不会停留在中间状态。
- 一致性(Consistency):事务执行前后,数据库的状态都要符合预设的规则,比如转账前后两个账户的总金额保持不变。
- 隔离性(Isolation):多个事务同时执行时,彼此之间不会互相干扰,每个事务都感觉不到其他事务的存在。
- 持久性(Durability):事务一旦提交成功,对数据库的修改就是永久性的,即使数据库出现故障也不会丢失。
PHP中事务处理的基本流程
在PHP中操作MySQL数据库时,我们可以通过PDO或者mysqli扩展来处理事务,两者的核心流程一致,都遵循以下步骤:
- 关闭数据库的自动提交模式,因为默认情况下MySQL执行每条SQL都会自动提交,我们需要手动控制提交时机。
- 开启事务,标记接下来的一组操作属于同一个事务。
- 依次执行事务中的多个数据库操作。
- 检查所有操作是否都执行成功:如果全部成功,就手动提交事务,让修改生效;如果有任意一个操作失败,就回滚事务,撤销所有已经执行的操作。
- 恢复数据库的自动提交模式(可选,根据业务场景决定)。
使用PDO处理事务的实例
PDO是PHP中常用的数据库扩展,支持多种数据库,下面以MySQL为例,演示转账场景的事务处理:
<?php
// 数据库配置
$host = '127.0.0.1';
$dbname = 'test_db';
$username = 'root';
$password = '123456';
try {
// 创建PDO连接,设置错误模式为异常,关闭自动提交
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, 0);
// 开启事务
$pdo->beginTransaction();
echo "事务已开启<br/>";
// 转出账户扣钱:假设用户A的id是1,要转出500元
$sql1 = "UPDATE user_account SET balance = balance - 500 WHERE id = 1 AND balance >= 500";
$res1 = $pdo->exec($sql1);
if ($res1 === false) {
throw new Exception("转出账户扣钱失败");
}
// 转入账户加钱:假设用户B的id是2,转入500元
$sql2 = "UPDATE user_account SET balance = balance + 500 WHERE id = 2";
$res2 = $pdo->exec($sql2);
if ($res2 === false) {
throw new Exception("转入账户加钱失败");
}
// 所有操作成功,提交事务
$pdo->commit();
echo "转账成功,事务已提交<br/>";
} catch (Exception $e) {
// 出现异常,回滚事务
if (isset($pdo) && $pdo->inTransaction()) {
$pdo->rollBack();
echo "操作失败,事务已回滚,错误信息:" . $e->getMessage() . "<br/>";
}
} finally {
// 恢复自动提交模式
if (isset($pdo)) {
$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, 1);
$pdo = null;
}
}
?>上面的代码中,我们首先关闭了自动提交,然后开启事务,接着执行两个更新操作。如果两个操作都成功,就提交事务;如果任意一个操作失败,就会抛出异常,进入catch块回滚事务,保证数据不会出现不一致的情况。最后在finally块中恢复自动提交并释放连接。
使用mysqli处理事务的实例
如果使用mysqli扩展,事务处理的逻辑和PDO类似,下面是同样的转账场景的mysqli实现:
<?php
// 数据库配置
$host = '127.0.0.1';
$username = 'root';
$password = '123456';
$dbname = 'test_db';
// 创建mysqli连接
$mysqli = new mysqli($host, $username, $password, $dbname);
if ($mysqli->connect_error) {
die("连接失败:" . $mysqli->connect_error);
}
// 设置字符集
$mysqli->set_charset("utf8");
// 关闭自动提交,开启事务
$mysqli->autocommit(false);
echo "事务已开启<br/>";
try {
// 转出账户扣钱
$sql1 = "UPDATE user_account SET balance = balance - 500 WHERE id = 1 AND balance >= 500";
$res1 = $mysqli->query($sql1);
if (!$res1 || $mysqli->affected_rows == 0) {
throw new Exception("转出账户扣钱失败");
}
// 转入账户加钱
$sql2 = "UPDATE user_account SET balance = balance + 500 WHERE id = 2";
$res2 = $mysqli->query($sql2);
if (!$res2) {
throw new Exception("转入账户加钱失败");
}
// 所有操作成功,提交事务
$mysqli->commit();
echo "转账成功,事务已提交<br/>";
} catch (Exception $e) {
// 出现异常,回滚事务
$mysqli->rollback();
echo "操作失败,事务已回滚,错误信息:" . $e->getMessage() . "<br/>";
} finally {
// 恢复自动提交
$mysqli->autocommit(true);
$mysqli->close();
}
?>mysqli的处理方式和PDO的核心区别是方法和属性名称不同,比如开启事务不需要显式调用beginTransaction,关闭自动提交后用autocommit(false)就相当于开启了事务,提交用commit,回滚用rollback,最后同样要恢复自动提交并关闭连接。
事务使用的注意事项
在使用PHP处理数据库事务时,有几个点需要特别注意:
- 并不是所有的数据库引擎都支持事务,比如MySQL的MyISAM引擎就不支持事务,只有InnoDB等引擎支持,所以使用前要确认表的存储引擎。
- 事务的范围不要太大,尽量只包含必要的操作,长时间未提交的事务会占用数据库资源,甚至导致锁表,影响其他操作。
- 事务中尽量避免执行耗时操作,比如复杂的查询、外部接口调用等,这些操作会延长事务的执行时间,增加风险。
- 如果事务中包含了DDL语句(比如CREATE、ALTER等),大部分数据库会自动提交当前事务,所以事务中尽量不要使用DDL语句。