You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何确保PHP+MySQL事务提交后,全部事务成功交由MySQL托管?

PHP+MySQL事务提交的可靠性保障

问题背景

我开发PHP应用时,使用mysqli_begin_transaction启动InnoDB事务返回true,后续一系列insert操作均检查错误码确认成功。但提交前执行非MySQL延迟操作期间,事务可能因死锁等原因被隐式回滚,而PHP未调用MySQL操作时无法感知。此时执行mysqli_commit会提交空事务,MySQL不报错,导致无法察觉事务已回滚。现需:

  1. 确保事务按预期提交的代码示例
  2. 确定性复现隐式回滚的方法

版本环境:MySQL 5.7.38-log、PHP 7.4


一、确保事务按预期提交的代码示例

核心思路:启用mysqli错误报告捕获驱动层异常,提交前主动检查事务状态,提交后可追加数据校验(可选但更严谨)。

<?php
// 启用mysqli错误报告,将MySQL错误转为PHP异常
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

try {
    $conn = new mysqli('localhost', 'username', 'password', 'dbname');
    $conn->set_charset('utf8mb4');

    // 启动事务
    $conn->begin_transaction();

    // 执行批量插入操作(示例)
    $stmt = $conn->prepare("INSERT INTO test_table (col1) VALUES (?)");
    $stmt->bind_param('s', $value);
    $values = ['val1', 'val2', 'val3'];
    foreach ($values as $value) {
        $stmt->execute();
        // 校验插入生效
        if ($stmt->affected_rows === 0) {
            throw new RuntimeException("插入数据失败,无行受影响");
        }
    }
    $stmt->close();

    // 模拟非MySQL延迟操作(如调用外部API、文件处理)
    sleep(5);

    // 提交前检查事务状态:@@in_transaction返回1表示事务仍在进行
    $result = $conn->query("SELECT @@in_transaction AS in_transaction");
    $row = $result->fetch_assoc();
    $result->free();
    if ((int)$row['in_transaction'] !== 1) {
        throw new RuntimeException("事务已被隐式回滚,无法提交");
    }

    // 提交事务
    $conn->commit();

    // 可选:最终数据校验,确保提交后数据持久化
    $checkResult = $conn->query("SELECT COUNT(*) AS count FROM test_table WHERE col1 IN ('val1','val2','val3')");
    $checkRow = $checkResult->fetch_assoc();
    $checkResult->free();
    if ((int)$checkRow['count'] !== 3) {
        throw new RuntimeException("事务提交后数据未完全写入");
    }

    echo "事务提交成功,数据已持久化";
} catch (mysqli_sql_exception $e) {
    // 捕获MySQL驱动异常(含死锁、隐式回滚等)
    if (isset($conn) && $conn->in_transaction) {
        $conn->rollback();
    }
    echo "MySQL错误:" . $e->getMessage();
} catch (RuntimeException $e) {
    // 捕获自定义业务异常
    if (isset($conn) && $conn->in_transaction) {
        $conn->rollback();
    }
    echo "业务错误:" . $e->getMessage();
} finally {
    if (isset($conn)) {
        $conn->close();
    }
}
?>

关键要点:

  • 启用MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT,让MySQL错误自动抛出异常,避免遗漏隐式回滚的通知
  • 提交前通过SELECT @@in_transaction确认事务状态,0则说明事务已被回滚
  • 可选的最终数据校验,应对极端场景下提交成功但数据未持久化的情况

二、确定性复现隐式回滚的方法

通过两个并发连接触发死锁,MySQL会自动回滚其中一个事务:

步骤1:创建测试表

CREATE TABLE test_deadlock (
    id INT PRIMARY KEY AUTO_INCREMENT,
    balance INT NOT NULL
) ENGINE=InnoDB;

INSERT INTO test_deadlock (balance) VALUES (100);

步骤2:事务1脚本(先运行)

<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$conn1 = new mysqli('localhost', 'username', 'password', 'dbname');
$conn1->set_charset('utf8mb4');

$conn1->begin_transaction();
// 持有id=1的行锁
$conn1->query("UPDATE test_deadlock SET balance = balance - 50 WHERE id = 1");

// 等待事务2触发死锁
sleep(10);

try {
    // 尝试获取id=2的锁(事务2已持有)
    $conn1->query("UPDATE test_deadlock SET balance = balance - 50 WHERE id = 2");
    $conn1->commit();
    echo "事务1提交成功";
} catch (mysqli_sql_exception $e) {
    echo "事务1错误:" . $e->getMessage();
    // 死锁后事务已被自动回滚,in_transaction返回false
    var_dump($conn1->in_transaction);
}
$conn1->close();
?>

步骤3:事务2脚本(事务1运行后立即启动)

<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$conn2 = new mysqli('localhost', 'username', 'password', 'dbname');
$conn2->set_charset('utf8mb4');

$conn2->begin_transaction();
// 插入并持有id=2的行锁
$conn2->query("INSERT INTO test_deadlock (balance) VALUES (100)");
$conn2->query("UPDATE test_deadlock SET balance = balance - 50 WHERE id = 2");

try {
    // 尝试获取id=1的锁(事务1已持有)
    $conn2->query("UPDATE test_deadlock SET balance = balance - 50 WHERE id = 1");
    $conn2->commit();
    echo "事务2提交成功";
} catch (mysqli_sql_exception $e) {
    echo "事务2错误:" . $e->getMessage();
}
$conn2->close();
?>

复现流程

  1. 先运行事务1脚本,在10秒延迟期间启动事务2脚本
  2. 两个事务形成循环锁,MySQL自动回滚其中一个(通常是后发起锁请求的事务)
  3. 被回滚的脚本会输出错误信息,且$conn->in_transaction为false,此时执行commit将提交空事务

内容的提问来源于stack exchange,提问作者user21266319

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 22:21:38