如何使用交易来确保PHP中的数据一致性?
在 PHP 中,为了确保数据一致性(尤其在多步写入数据库的操作中),我们使用 数据库事务(transaction)。事务可以保证要么全部成功,要么全部失败,防止因部分执行成功而导致数据异常。
一、什么是事务(Transaction)
事务是一组 SQL 操作的集合,这些操作要么全部执行,要么一个也不执行。事务具有四大特性,简称 ACID:
| 特性 | 含义 |
|---|---|
| 原子性(Atomicity) | 操作不可分割,全部成功或全部失败 |
| 一致性(Consistency) | 执行前后数据满足一致性规则 |
| 隔离性(Isolation) | 多个事务之间互不干扰 |
| 持久性(Durability) | 提交后结果永久保存 |
二、在 PHP 中使用事务的前提条件
必须使用支持事务的数据库引擎,如 MySQL 的 InnoDB(❌ MyISAM 不支持事务)。
三、使用 PDO 执行事务(推荐)
示例:转账操作(A → B)
<?php
$pdo = new PDO("mysql:host=localhost;dbname=test", "root", "");
// 设置为抛出异常
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
// 开启事务
$pdo->beginTransaction();
// 用户 A 扣款
$stmt1 = $pdo->prepare("UPDATE users SET balance = balance - :amount WHERE id = :from");
$stmt1->execute([':amount' => 100, ':from' => 1]);
// 用户 B 加款
$stmt2 = $pdo->prepare("UPDATE users SET balance = balance + :amount WHERE id = :to");
$stmt2->execute([':amount' => 100, ':to' => 2]);
// 提交事务
$pdo->commit();
echo "转账成功!";
} catch (Exception $e) {
// 回滚事务
$pdo->rollBack();
echo "转账失败:" . $e->getMessage();
}
一旦任意 SQL 报错(比如余额不足或语法错误),事务会回滚,保证不会发生“扣了 A 没加到 B”的数据错乱。
四、事务失效的常见原因
| 原因 | 描述 |
|---|---|
| 使用了 MyISAM 表 | 不支持事务,需改为 InnoDB |
| 没启用异常处理 | PDO 默认不抛异常,需手动设置 ERRMODE_EXCEPTION |
| 部分语句不在事务中 | 比如某些写操作在事务开始之外 |
| 写操作自动提交 | 如 autocommit 没关闭或写在非事务内 |
五、使用 mysqli 事务(较低级)
$conn = new mysqli("localhost", "root", "", "test");
$conn->autocommit(false); // 关闭自动提交
try {
$conn->query("UPDATE users SET balance = balance - 100 WHERE id = 1");
$conn->query("UPDATE users SET balance = balance + 100 WHERE id = 2");
$conn->commit();
echo "转账成功";
} catch (Exception $e) {
$conn->rollback();
echo "转账失败:" . $e->getMessage();
}
六、实战建议
| 场景 | 是否建议用事务 |
|---|---|
| 用户注册(多表) | ✅ 是 |
| 用户发帖(帖子 + 附件) | ✅ 是 |
| 电商下单(订单、库存、支付) | ✅ 必须 |
| 仅查询操作 | ❌ 不建议(无意义) |
七、安全与性能提示
- 尽量控制事务时间(避免事务期间长时间等待用户输入)
- 避免在事务中执行过多非必要逻辑
- 使用死锁处理逻辑(如捕获后重试)
📚 八、权威参考资料
总结:事务使用流程(PDO)
try {
$pdo->beginTransaction();
// 写操作 1
// 写操作 2
$pdo->commit(); // 一起成功
} catch (Exception $e) {
$pdo->rollBack(); // 任一失败,全体失败
}
更多详细内容,请关注其他相关文章!