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

PHP PDO添加重试逻辑解决死锁后遇活跃事务错误求助

解决PDO持久连接下事务重试导致的「已有活跃事务」错误

问题根源

  • 持久化连接的事务残留:PDO持久连接会在进程间复用,若某连接上的事务因异常未正确回滚,后续复用该连接的脚本调用beginTransaction()时就会触发报错。
  • 重试逻辑的事务清理漏洞:当前代码仅在非死锁异常时回滚事务,死锁触发重试时直接continue,未回滚失败的事务,导致事务一直处于活跃状态,下一次循环执行beginTransaction()就会报错。
  • 多独立脚本的连接复用风险:prepare.php通过shell_exec启动40个独立进程,这些进程复用连接池中的连接时,可能继承了前一个进程未关闭的事务。

修复方案

1. 完善重试逻辑的事务清理

无论是否是死锁异常,事务失败后必须先回滚再重试或退出。修改插入脚本的异常处理:

$retryAttempts = 3;
$retryDelay = 100000; // 微秒(0.1秒)

for ($attempt = 1; $attempt <= $retryAttempts; $attempt++) {
    try {
        // 先检查并清理当前连接的活跃事务
        if ($pdo_conn->inTransaction()) {
            $pdo_conn->rollback();
        }
        
        $pdo_conn->beginTransaction();
        $stmt = $pdo_conn->prepare($insert_query);
        $stmt->execute();
        $pdo_conn->commit();
        break;
    } catch (PDOException $e) {
        // 任何异常都先回滚事务
        if ($pdo_conn->inTransaction()) {
            $pdo_conn->rollback();
        }
        
        if (($e->getCode() == "1213" || $e->getCode() == "40001") && $attempt < $retryAttempts) {
            echo "触发死锁,进行第{$attempt}次重试" . PHP_EOL;
            usleep($retryDelay); 
            continue;
        } else {
            echo "事务失败:" . $e->getMessage() . PHP_EOL;
            break;
        }
    }
}

2. 优化持久连接的使用

如果业务允许,关闭持久连接(移除PDO::ATTR_PERSISTENT => true),彻底避免连接复用带来的问题。若必须使用持久连接,在初始化连接后强制清理残留事务:

// connection.php 初始化连接后添加
if ($pdo_conn->inTransaction()) {
    $pdo_conn->rollback();
}

3. 独立脚本的连接隔离

每个scripts_{$i}.php完成操作后主动销毁连接,确保连接池中的连接无事务残留:

// 插入操作完成后添加
$pdo_conn = null;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 05:12:51