怎样在PHP项目中实现数据回滚?

wen java案例 10

本文目录导读:

怎样在PHP项目中实现数据回滚?

  1. 使用 PDO 实现事务回滚(最推荐)
  2. 使用 MySQLi 实现事务回滚
  3. 使用 ORM 框架(如 Laravel、ThinkPHP)
  4. 跨服务 / 多数据库事务回滚(分布式事务)
  5. 数据库引擎要求
  6. 最佳实践建议

在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()
  • 数据库连接必须持久或重用同一个连接(事务是基于连接的)。

抱歉,评论功能暂时关闭!