本文目录导读:

在PHP项目中实现数据回滚,主要依靠数据库事务(Transaction)机制,事务允许你将一组数据库操作(如插入、更新、删除)打包成一个原子单元:要么全部成功执行(提交),要么全部失败回滚(撤销)。
以下是几种常见的实现方式,从基础到进阶:
使用 PDO 实现事务回滚(最推荐)
PDO(PHP Data Objects)提供了简洁、统一的数据库事务控制。
<?php
try {
// 1. 创建 PDO 连接,并设置错误模式为异常
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 2. 关闭自动提交,开启事务
$pdo->beginTransaction();
// 3. 执行多个数据库操作(转账操作)
// 从账户 A 扣钱
$sql1 = "UPDATE accounts SET balance = balance - 100 WHERE id = 1";
$pdo->exec($sql1);
// 向账户 B 加钱
$sql2 = "UPDATE accounts SET balance = balance + 100 WHERE id = 2";
$pdo->exec($sql2);
// 4. 如果没有异常,提交事务
$pdo->commit();
echo "转账成功!";
} catch (PDOException $e) {
// 5. 如果任何一步出错,回滚事务
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
echo "转账失败,已回滚:" . $e->getMessage();
}
?>
关键点:
beginTransaction():开启事务,PDO 会暂时关闭自动提交。commit():所有操作成功,将更改永久写入数据库。rollBack():发生异常,撤销本次事务中的所有操作。inTransaction():检查当前是否在事务中,避免在非事务状态下调用rollBack()引发错误。
使用 MySQLi 实现事务回滚
如果你仍在使用 MySQLi 扩展,也可以实现类似功能:
<?php
$mysqli = new mysqli('localhost', 'root', 'password', 'test');
if ($mysqli->connect_error) {
die("连接失败: " . $mysqli->connect_error);
}
// 关闭自动提交
$mysqli->autocommit(FALSE);
try {
// 执行操作
$result1 = $mysqli->query("UPDATE accounts SET balance = balance - 100 WHERE id = 1");
$result2 = $mysqli->query("UPDATE accounts SET balance = balance + 100 WHERE id = 2");
if ($result1 && $result2) {
$mysqli->commit();
echo "提交成功";
} else {
throw new Exception($mysqli->error);
}
} catch (Exception $e) {
$mysqli->rollback();
echo "回滚成功:" . $e->getMessage();
}
$mysqli->close();
?>
使用 ORM 框架(如 Laravel、ThinkPHP)
现代框架通常封装了事务机制,使用更便捷,以 Laravel 为例:
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
// 所有操作默认在一个事务中
DB::table('accounts')->where('id', 1)->decrement('balance', 100);
DB::table('accounts')->where('id', 2)->increment('balance', 100);
// 如果这里抛出异常,框架会自动回滚
});
如果需要手动控制提交/回滚:
DB::beginTransaction();
try {
// 操作...
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
// 记录日志或返回错误
}
ThinkPHP 6 示例:
Db::transaction(function () {
Db::table('accounts')->where('id', 1)->dec('balance', 100);
Db::table('accounts')->where('id', 2)->inc('balance', 100);
});
跨服务 / 多数据库事务回滚(分布式事务)
当你的操作涉及多个数据库或不同服务(如 MySQL + Redis + 第三方 API),单数据库事务就不够了,常见的解决方案:
- 两阶段提交(2PC):协调器先准备所有参与者,再统一提交或回滚,实现复杂,性能有损耗。
- Saga 模式:将大事务拆分为多个本地子事务,每个子事务有对应的补偿操作(回滚逻辑),适用于微服务。
- 消息队列 + 最终一致性:先执行核心操作,失败时通过消息队列触发补偿或异步重试。
PHP 中简单实现补偿逻辑示例(手动回滚):
$completedSteps = [];
try {
// 步骤1:更新 MySQL
$pdo->exec("UPDATE ...");
$completedSteps[] = 'mysql';
// 步骤2:删除 Redis 缓存
$redis->del('key');
$completedSteps[] = 'redis';
// 步骤3:调用外部 API
$api->transfer(...);
$completedSteps[] = 'api';
} catch (\Exception $e) {
// 根据已完成的步骤,逆向执行补偿操作
if (in_array('api', $completedSteps)) {
$api->rollbackTransfer(...); // 调用外部API的撤销接口
}
if (in_array('redis', $completedSteps)) {
$redis->set('key', $oldValue); // 恢复旧值
}
if (in_array('mysql', $completedSteps)) {
$pdo->exec("ROLLBACK"); // MySQL 事务回滚
}
// 记录错误日志
}
数据库引擎要求
事务回滚的前提是数据库表引擎支持事务:
- MySQL:使用
InnoDB引擎(MyISAM不支持事务)。 - PostgreSQL:默认支持。
- SQLite:支持但写性能较弱。
检查/修改 MySQL 表引擎:
-- 查看引擎 SHOW TABLE STATUS LIKE 'your_table'; -- 改为 InnoDB ALTER TABLE your_table ENGINE = InnoDB;
最佳实践建议
| 场景 | 推荐方案 |
|---|---|
| 简单 PHP 项目 | PDO 事务(轻量、安全、跨数据库) |
| 使用框架 | 框架自带事务(Laravel DB::transaction(),ThinkPHP Db::transaction()) |
| 跨服务/分布式 | Saga 模式 或 消息队列最终一致性 |
| 复杂业务逻辑 | 封装一个 TransactionManager 类,统一管理 begin/commit/rollback |
核心原则:
- 所有写操作(增、删、改)尽量放在事务中。
- 事务内不要包含耗时的外部调用(如 HTTP 请求、文件读写),以免锁表。
- 记得捕获异常,确保任何失败都能触发
rollBack()。 - 数据库连接必须持久或重用同一个连接(事务是基于连接的)。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播